|
From: Zaumseil R. <RZa...@kk...> - 2025-11-07 14:19:17
|
Hi Eric
I really like the tcl::unsupported::assemble approach.
It opens the way to add more commands/sugar to tcl itself without
having to hard code it in C.
Now we have to agree on the syntax. May be a wiki page with a summary of the different proposals?
Regards
rene
Von: EricT <tw...@gm...>
Gesendet: Freitag, 7. November 2025 01:03
An: Zaumseil René <RZa...@kk...>; tcl...@li...
Betreff: Re: [TCLCORE] [Ext] Re: [=] for concise expressions (was Re: TIP 672 Implementation Complete - Ready for Sponsorship)
Hi Rene,
I was experimenting with your TIP 674 prototype and realized both your let command and Colin's expression evaluator share something important: they could both be implemented in pure Tcl if tcl::unsupported::assemble were promoted to supported status with handle-based caching.
This would avoid another round of "expr wars" where lack of consensus leaves us with nothing. Instead of debating whose syntax is better, we'd each have the power to create our own Design-Specific Language. With bytecode caching, pure Tcl implementations achieve C extension performance - Colin's evaluator runs at 1.8 microseconds, nearly as fast as expr.
Why does this matter for your TIP? If let performs just as well in Tcl as in C, why require a C implementation at all? C extensions take weeks to develop, need binaries for every platform/architecture combination, and ultimately call the same bytecode infrastructure that assemble uses. A supported bytecode API would make both proposals - and many others - viable without C.
Perhaps we should advocate together for making the bytecode compilation infrastructure officially supported, rather than competing for TIP approval on individual commands?
Best,
Eric
On Wed, Nov 5, 2025 at 11:31 PM Zaumseil René via Tcl-Core <tcl...@li...<mailto:tcl...@li...>> wrote:
Hello
I also like the = approach as a new expr command without need of $ for variables.
But I'm not sure about the currently proposed syntax.
Is it "= 1 + 2" or "= 1+2" (with or without spaces)?
If it is the later then I would propose to use some of the syntax of the "let" from tip 674.
Some syntax ideas:
"= 1+2" return 3, simple case
"= a 1+2 b 2+4 c a+b" return {3 6 9} and set a,b,c
"= 1+2 = 2+4" return {3 6}
And may be some others…
Regards
rene
Von: EricT <tw...@gm...<mailto:tw...@gm...>>
Gesendet: Mittwoch, 5. November 2025 23:22
An: Colin Macleod <col...@ya...<mailto:col...@ya...>>; tcl...@li...<mailto:tcl...@li...>
Betreff: [Ext] Re: [TCLCORE] [=] for concise expressions (was Re: TIP 672 Implementation Complete - Ready for Sponsorship)
Hi Colin,
I've successfully modified your amazing code to handle arrays. In doing so, I also found 2 other issues, one is with your Boolean check, the other with your function name check, both because of [string is] issues.
- Boolean check: `$token eq "false" || $token eq "true"` (was `[string is boolean $token]` - treated 'f','n', 't', 'y', etc. variables as boolean false, no, true, yes, ...)
- Function check: `[regexp {^[[:alpha:]]} $token]` (was `[string is alpha $token]` - broke log10, atan2)
here's the code for arrays:
# Function call or array reference?
set nexttok [lindex $::tokens $::tokpos]
if {$nexttok eq "(" && [regexp {^[[:alpha:]]} $token]} {
set fun [namespace which tcl::mathfunc::$token]
if {$fun ne {}} {
# It's a function
incr ::tokpos
set opcodes "push $fun; "
append opcodes [parseFuncArgs]
return $opcodes
} else {
# Not a function, assume array reference
incr ::tokpos
set opcodes "push $token; "
# Parse the index expression - leaves VALUE on stack
append opcodes [parse 0]
# Expect closing paren
set closing [lindex $::tokens $::tokpos]
if {$closing ne ")"} {
error "Calc: expected ')' but found '$closing'"
}
# Stack now has: [arrayname, indexvalue]
incr ::tokpos
append opcodes "loadArrayStk; "
return $opcodes
}
}
In addition, there has indeed been some changes in the bytecode, land and lor are no longer supported in 9.0 although they work in 8.6.
I had an AI generate some 117 test cases, which all pass on 8.6 and 111 on 9.x (the land/lor not being tested in 9.x).
Colin, with your permission, I can post the code as a new file, with all the test cases, say on a repository at github.
I think a new TIP is worth considering; one that promotes assemble to a supported form, with a compile and handle approach to avoid the time parsing the ascii byte code text. I think that this would be great for your = command, but also quite useful for others who might want to create their own little languages.
By doing it this way, it remains pure tcl, and avoids all the problems with different systems and hardware that a binary extension would create. In the end, I believe your code can achieve performance parity with expr. Not only does it remove half the [expr {...}] baggage, but all the $'s too! So much easier on these old eyes.
Regards,
Eric
On Tue, Nov 4, 2025 at 1:06 PM EricT <tw...@gm...<mailto:tw...@gm...>> wrote:
Hi Colin,
Hmmm, why can't you do bareword on $a(b) as a(b) you just need to do an uplevel to see if a is a variable, if not, it would have to be a function. True?
% tcl::unsupported::disassemble script {set a [expr {$b($c)}] }
snip
Command 2: "expr {$b($c)}..."
(2) push1 1 # "b"
(4) push1 2 # "c"
(6) loadStk
(7) loadArrayStk
(8) tryCvtToNumeric
(9) storeStk
(10) done
This doesn't look too much different from what you are producing.
I think what's really needed here is a TIP that would open up the bytecode a bit so you don't need to use an unsupported command. And then maybe even have a new command to take the string byte code you are now producing and return a handle to a cached version that was probably equivalent to the existing bytecode. Then your cache array would be
set cache($exp) $handle
Instead of it having to parse the text, it could be as fast as bytecode. You'd likely be just as fast as expr, and safe as well, since you can't pass a string command in where the bareword is required:
% set x {[pwd]}
[pwd]
% = sqrt(x)
exp= |sqrt(x)| code= |push ::tcl::mathfunc::sqrt; push x; loadStk; invokeStk 2; | ifexist: 0
expected floating-point number but got "[pwd]"
I think you really have something here, perhaps this is the best answer yet to slay the expr dragon!
Regards,
Eric
On Tue, Nov 4, 2025 at 6:52 AM Colin Macleod via Tcl-Core <tcl...@li...<mailto:tcl...@li...>> wrote:
Hi Eric,
That's very neat!
Yes, a pure Tcl version could go into TclLib. I still think it may be worth trying a C implementation though. The work-around that's needed for array references [= 2* $a(b)] would defeat the caching, so it would be good to speed up the parsing if possible. Also I think your caching may be equivalent to doing byte-compilation, in which case it may make sense to use the framework which already exists for that.
Colin.
On 04/11/2025 01:18, EricT wrote:
that is:
if {[info exist ::cache($exp)]} {
tailcall ::tcl::unsupported::assemble $::cache($exp)
}
(hate gmail!)
On Mon, Nov 3, 2025 at 5:17 PM EricT <tw...@gm...<mailto:tw...@gm...>> wrote:
and silly of me, it should be:
if {[info exist ::cache($exp)]} {
tailcall ::tcl::unsupported::assemble $::cache($exp)
}
On Mon, Nov 3, 2025 at 4:50 PM EricT <tw...@gm...<mailto:tw...@gm...>> wrote:
With a debug line back in plus the tailcall:
proc = args {
set exp [join $args]
if { [info exist ::cache($exp)] } {
return [tailcall ::tcl::unsupported::assemble $::cache($exp)]
}
set tokens [tokenise $exp]
deb1 "TOKENS = '$tokens'"
set code [compile $tokens]
deb1 "GENERATED CODE:\n$code\n"
puts "exp= |$exp| code= |$code| ifexist: [info exist ::cache($exp)]"
set ::cache($exp) $code
uplevel [list ::tcl::unsupported::assemble $code]
}
% set a 5
5
% set b 10
10
% = a + b
exp= |a + b| code= |push a; loadStk; push b; loadStk; add; | ifexist: 0
15
% = a + b
15
% time {= a + b} 1000
1.73 microseconds per iteration
Faster still!
I thought the uplevel was needed to be able to get the local variables, seems not.
% proc foo arg {set a 5; set b 10; set c [= a+b+arg]}
% foo 5
exp= |a+b+arg| code= |push a; loadStk; push b; loadStk; add; push arg; loadStk; add; | ifexist: 0
20
% foo 5
20
% proc foo arg {global xxx; set a 5; set b 10; set c [= a+b+arg+xxx]}
% set xxx 100
100
% foo 200
315
% time {foo 200} 10000
2.1775 microseconds per iteration
% parray cache
cache(a + b) = push a; loadStk; push b; loadStk; add;
cache(a+b+arg) = push a; loadStk; push b; loadStk; add; push arg; loadStk; add;
cache(a+b+arg+xxx) = push a; loadStk; push b; loadStk; add; push arg; loadStk; add; push xxx; loadStk; add;
Very Impressive, great job Colin! Great catch Don!
Eric
On Mon, Nov 3, 2025 at 4:22 PM Donald Porter via Tcl-Core <tcl...@li...<mailto:tcl...@li...>> wrote:
Check what effect replacing [uplevel] with [tailcall] has.
On Nov 3, 2025, at 7:13 PM, EricT <tw...@gm...<mailto:tw...@gm...>> wrote:
Subject: Your bytecode expression evaluator - impressive results with caching!
Hey Colin:
I took a look at your bytecode-based expression evaluator and was intrigued by the approach. I made a small modification to add caching and the results are really impressive. Here's what I changed:
proc = args {
set exp [join $args]
if {[info exist ::cache($exp)]} {
return [uplevel [list ::tcl::unsupported::assemble $::cache($exp)]]
}
set tokens [tokenise $exp]
deb1 "TOKENS = '$tokens'"
set code [compile $tokens]
deb1 "GENERATED CODE:\n$code\n"
set ::cache($exp) $code
uplevel [list ::tcl::unsupported::assemble $code]
}
The cache is just a simple array lookup - one line to store, one line to retrieve. But the performance impact is huge:
Performance Tests
Without caching
% time {= 1 + 2} 1000
24.937 microseconds per iteration
With caching
% time {= 1 + 2} 1000
1.8 microseconds per iteration
That's a 13x speedup! The tokenize and parse steps were eating about 92% of the execution time.
The Real Magic: Bare Variables + Caching
What really impressed me is how well your bare variable feature synergizes with caching:
% set a 5
5
% set b 6
6
% = a + b
11
% time {= a + b} 1000
2.079 microseconds per iteration
Now change the variable values
% set a 10
10
% = a + b
16
% time {= a + b} 1000
2.188 microseconds per iteration
The cache entry stays valid even when the variable values change! Why? Because the bytecode stores variable names, not values:
push a; loadStk; push b; loadStk; add;
The loadStk instruction does runtime lookup, so:
- Cache key is stable: "a + b"
- Works for any values of a and b
- One cache entry handles all value combinations
Compare this to if we used $-substitution:
= $a + $b # With a=5, b=6 becomes "5 + 6"
= $a + $b # With a=10, b=6 becomes "10 + 6" - different cache key!
Every value change would create a new cache entry or worse, a cache miss.
Comparison to Other Approaches
Tcl's expr: about 0.40 microseconds
Direct C evaluator: about 0.53 microseconds
Your cached approach: about 1.80 microseconds
Your uncached approach: about 24.9 microseconds
With caching, you're only 3-4x slower than a direct C evaluator.
My Assessment
Your design is excellent. The bare variable feature isn't just syntax sugar - it's essential for good cache performance. The synergy between:
1. Bare variables leading to stable cache keys
2. Runtime lookup keeping cache hot
3. Simple caching providing dramatic speedup
makes this really elegant.
My recommendation: Keep it in Tcl! The implementation is clean, performance is excellent (1.8 microseconds is plenty fast), and converting to C would add significant complexity for minimal gain (maybe getting to about 1.0 microseconds).
The Tcl prototype with caching is actually the right solution here. Sometimes the prototype IS the product!
Excellent work on this. The bytecode approach really shines with caching enabled.
On Sun, Nov 2, 2025 at 10:14 AM Colin Macleod via Tcl-Core <tcl...@li...<mailto:tcl...@li...>> wrote:
Hi again,
I've now made a slightly more serious prototype, see https://cmacleod.me.uk/tcl/expr_ng
This is a modified version of the prototype I wrote for tip 676. It's still in Tcl, but doesn't use `expr`. It tokenises and parses the input, then generates TAL bytecode and uses ::tcl::unsupported::assemble to run that. A few examples:
(bin) 100 % set a [= 3.0/4]
0.75
(bin) 101 % set b [= sin(a*10)]
0.9379999767747389
(bin) 102 % set c [= (b-a)*100]
18.79999767747389
(bin) 103 % namespace eval nn {set d [= 10**3]}
1000
(bin) 104 % set e [= a?nn::d:b]
1000
(bin) 105 % = {3 + [pwd]}
Calc: expected start of expression but found '[pwd]'
(bin) 106 % = {3 + $q}
Calc: expected start of expression but found '$q'
(bin) 107 % = sin (12)
-0.5365729180004349
(bin) 108 % array set rr {one 1 two 2 three 3}
(bin) 110 % = a * rr(two)
Calc: expected operator but found '('
(bin) 111 % = a * $rr(two)
1.5
- You can use $ to get an array value substituted before the `=` code sees the expression.
(bin) 112 % string repeat ! [= nn::d / 15]
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Colin.
On 02/11/2025 09:04, Donal Fellows wrote:
Doing the job properly would definitely involve changing the expression parser, with my suggested fix being to turn all bare words not otherwise recognised as constants or in positions that look like function calls (it's a parser with some lookahead) into simple variable reads (NB: C resolves such ambiguities within itself differently, but that's one of the nastiest parts of the language). We would need to retain $ support for resolving ambiguity (e.g., array reads vs function calls; you can't safely inspect the interpreter to resolve it at the time of compiling the expression due to traces and unknown handlers) as well as compatibility, but that's doable as it is a change only in cases that are currently errors.
Adding assignment is quite a bit trickier, as that needs a new major syntax class to describe the left side of the assignment. I suggest omitting that from consideration at this stage.
Donal.
-------- Original message --------
From: Colin Macleod via Tcl-Core <tcl...@li...><mailto:tcl...@li...>
Date: 02/11/2025 08:13 (GMT+00:00)
To: Pietro Cerutti <ga...@ga...><mailto:ga...@ga...>
Cc: tcl...@li...<mailto:tcl...@li...>, av...@lo...<mailto:av...@lo...>
Subject: Re: [TCLCORE] Fwd: TIP 672 Implementation Complete - Ready for Sponsorship
Indeed, this toy implementation doesn't handle that:
% = sin (12)
can't read "sin": no such variable
I'm not sure that's serious, but it could be fixed in a C implementation.
_______________________________________________
Tcl-Core mailing list
Tcl...@li...<mailto:Tcl...@li...>
https://lists.sourceforge.net/lists/listinfo/tcl-core
_______________________________________________
Tcl-Core mailing list
Tcl...@li...<mailto:Tcl...@li...>
https://lists.sourceforge.net/lists/listinfo/tcl-core
_______________________________________________
Tcl-Core mailing list
Tcl...@li...<mailto:Tcl...@li...>
https://lists.sourceforge.net/lists/listinfo/tcl-core
_______________________________________________
Tcl-Core mailing list
Tcl...@li...<mailto:Tcl...@li...>
https://lists.sourceforge.net/lists/listinfo/tcl-core
_______________________________________________
Tcl-Core mailing list
Tcl...@li...<mailto:Tcl...@li...>
https://lists.sourceforge.net/lists/listinfo/tcl-core
_______________________________________________
Tcl-Core mailing list
Tcl...@li...<mailto:Tcl...@li...>
https://lists.sourceforge.net/lists/listinfo/tcl-core
|
|
From: EricT <tw...@gm...> - 2025-11-07 20:35:19
|
Rene,
That's the beauty of making bytecode compilation official - we DON'T have
to agree on syntax! With a supported tcl::bytecode API, each of us can
create our own Design-Specific Language for our own needs. No more "one
size fits all" debates that go nowhere.
That said, I think you might find Colin's approach already fits your needs
quite well. Here's how your examples would look:
Multiple assignments:
# Your let syntax
let {x 1+2 y 3*4 z $x+$y}
# Colin's syntax (bare variables, no $)
: {x = 1+2
y = 3*4
z = x+y}
For my own taste, I've here aliased = to : since = as the command seems to
clash visually with = for assignment, especially in cases like a = b = c =
d. The semicolon requires braces since it's special to Tcl, but by mapping
newlines to semicolons internally, multiple statements work naturally in
braced blocks as shown above.
Returning multiple values:
# With a simple helper function
proc tcl::mathfunc::gather {args} { list {*}$args }
# Your canvas example
.c create text {*}[= gather(x+1, y+1)] -text a
# Or inline
= {tx = x+1 ; ty = y+1 ; gather(tx, ty)}
Your calculations example:
set i 0.5
= {x = sin(i)
y = cos(i) + x
z = x + y}
The key differences: bare variables (no $), explicit = for assignment,
semicolon (or newline) to separate multiple expressions, and gather() for
multiple returns. But it's all extensible - add your own functions to
tcl::mathfunc:: and they're automatically available.
If you prefer different syntax, you can build your let command using the
same bytecode infrastructure. That's the whole point - multiple DSLs
coexisting, each optimized by bytecode caching.
Eric
On Fri, Nov 7, 2025 at 6:19 AM Zaumseil René via Tcl-Core <
tcl...@li...> wrote:
> Hi Eric
>
>
>
> I really like the tcl::unsupported::assemble approach.
>
> It opens the way to add more commands/sugar to tcl itself without
>
> having to hard code it in C.
>
>
>
> Now we have to agree on the syntax. May be a wiki page with a summary of
> the different proposals?
>
>
>
>
>
> Regards
>
> rene
>
>
>
>
>
> *Von:* EricT <tw...@gm...>
> *Gesendet:* Freitag, 7. November 2025 01:03
> *An:* Zaumseil René <RZa...@kk...>; tcl...@li...
> *Betreff:* Re: [TCLCORE] [Ext] Re: [=] for concise expressions (was Re:
> TIP 672 Implementation Complete - Ready for Sponsorship)
>
>
>
> Hi Rene,
>
>
>
> I was experimenting with your TIP 674 prototype and realized both your let command and Colin's expression evaluator share something important: they could both be implemented in pure Tcl if tcl::unsupported::assemble were promoted to supported status with handle-based caching.
>
>
>
> This would avoid another round of "expr wars" where lack of consensus leaves us with nothing. Instead of debating whose syntax is better, we'd each have the power to create our own Design-Specific Language. With bytecode caching, pure Tcl implementations achieve C extension performance - Colin's evaluator runs at 1.8 microseconds, nearly as fast as expr.
>
>
>
> Why does this matter for your TIP? If let performs just as well in Tcl as in C, why require a C implementation at all? C extensions take weeks to develop, need binaries for every platform/architecture combination, and ultimately call the same bytecode infrastructure that assemble uses. A supported bytecode API would make both proposals - and many others - viable without C.
>
>
>
> Perhaps we should advocate together for making the bytecode compilation infrastructure officially supported, rather than competing for TIP approval on individual commands?
>
>
>
> Best,
>
> Eric
>
>
>
>
>
> On Wed, Nov 5, 2025 at 11:31 PM Zaumseil René via Tcl-Core <
> tcl...@li...> wrote:
>
> Hello
>
>
>
> I also like the = approach as a new expr command without need of $ for
> variables.
>
> But I'm not sure about the currently proposed syntax.
>
> Is it "= 1 + 2" or "= 1+2" (with or without spaces)?
>
> If it is the later then I would propose to use some of the syntax of the
> "let" from tip 674.
>
>
>
> Some syntax ideas:
>
> "= 1+2" return 3, simple case
>
> "= a 1+2 b 2+4 c a+b" return {3 6 9} and set a,b,c
>
> "= 1+2 = 2+4" return {3 6}
>
> And may be some others…
>
>
>
> Regards
>
> rene
>
> *Von:* EricT <tw...@gm...>
> *Gesendet:* Mittwoch, 5. November 2025 23:22
> *An:* Colin Macleod <col...@ya...>;
> tcl...@li...
> *Betreff:* [Ext] Re: [TCLCORE] [=] for concise expressions (was Re: TIP
> 672 Implementation Complete - Ready for Sponsorship)
>
>
>
> Hi Colin,
>
>
>
> I've successfully modified your amazing code to handle arrays. In doing so, I also found 2 other issues, one is with your Boolean check, the other with your function name check, both because of [string is] issues.
>
>
>
> - Boolean check: `$token eq "false" || $token eq "true"` (was `[string is boolean $token]` - treated 'f','n', 't', 'y', etc. variables as boolean false, no, true, yes, ...)
>
>
>
> - Function check: `[regexp {^[[:alpha:]]} $token]` (was `[string is alpha $token]` - broke log10, atan2)
>
>
>
>
>
> here's the code for arrays:
>
>
>
> # Function call or array reference?
>
> set nexttok [lindex $::tokens $::tokpos]
>
> if {$nexttok eq "(" && [regexp {^[[:alpha:]]} $token]} {
>
> set fun [namespace which tcl::mathfunc::$token]
>
> if {$fun ne {}} {
>
> # It's a function
>
> incr ::tokpos
>
> set opcodes "push $fun; "
>
> append opcodes [parseFuncArgs]
>
> return $opcodes
>
> } else {
>
> # Not a function, assume array reference
>
> incr ::tokpos
>
> set opcodes "push $token; "
>
> # Parse the index expression - leaves VALUE on stack
>
> append opcodes [parse 0]
>
> # Expect closing paren
>
> set closing [lindex $::tokens $::tokpos]
>
> if {$closing ne ")"} {
>
> error "Calc: expected ')' but found '$closing'"
>
> }
>
> # Stack now has: [arrayname, indexvalue]
>
> incr ::tokpos
>
> append opcodes "loadArrayStk; "
>
> return $opcodes
>
> }
>
> }
>
>
>
>
>
> In addition, there has indeed been some changes in the bytecode, land and lor are no longer supported in 9.0 although they work in 8.6.
>
>
>
> I had an AI generate some 117 test cases, which all pass on 8.6 and 111 on 9.x (the land/lor not being tested in 9.x).
>
>
>
> Colin, with your permission, I can post the code as a new file, with all the test cases, say on a repository at github.
>
>
>
> I think a new TIP is worth considering; one that promotes assemble to a supported form, with a compile and handle approach to avoid the time parsing the ascii byte code text. I think that this would be great for your = command, but also quite useful for others who might want to create their own little languages.
>
>
>
> By doing it this way, it remains pure tcl, and avoids all the problems with different systems and hardware that a binary extension would create. In the end, I believe your code can achieve performance parity with expr. Not only does it remove half the [expr {...}] baggage, but all the $'s too! So much easier on these old eyes.
>
>
>
> Regards,
>
>
>
> Eric
>
>
>
>
>
> On Tue, Nov 4, 2025 at 1:06 PM EricT <tw...@gm...> wrote:
>
> Hi Colin,
>
>
>
> Hmmm, why can't you do bareword on $a(b) as a(b) you just need to do an
> uplevel to see if a is a variable, if not, it would have to be a function.
> True?
>
>
>
> % tcl::unsupported::disassemble script {set a [expr {$b($c)}] }
> snip
>
> Command 2: "expr {$b($c)}..."
> (2) push1 1 # "b"
> (4) push1 2 # "c"
> (6) loadStk
> (7) loadArrayStk
> (8) tryCvtToNumeric
> (9) storeStk
> (10) done
>
> This doesn't look too much different from what you are producing.
>
>
>
> I think what's really needed here is a TIP that would open up the bytecode
> a bit so you don't need to use an unsupported command. And then maybe even
> have a new command to take the string byte code you are now producing and
> return a handle to a cached version that was probably equivalent to the
> existing bytecode. Then your cache array would be
>
>
>
> set cache($exp) $handle
>
>
>
> Instead of it having to parse the text, it could be as fast as bytecode.
> You'd likely be just as fast as expr, and safe as well, since you can't
> pass a string command in where the bareword is required:
>
>
>
> % set x {[pwd]}
> [pwd]
> % = sqrt(x)
> exp= |sqrt(x)| code= |push ::tcl::mathfunc::sqrt; push x; loadStk;
> invokeStk 2; | ifexist: 0
> expected floating-point number but got "[pwd]"
>
> I think you really have something here, perhaps this is the best answer
> yet to slay the expr dragon!
>
>
>
> Regards,
>
>
>
> Eric
>
>
>
>
>
> On Tue, Nov 4, 2025 at 6:52 AM Colin Macleod via Tcl-Core <
> tcl...@li...> wrote:
>
> Hi Eric,
>
> That's very neat!
>
> Yes, a pure Tcl version could go into TclLib. I still think it may be
> worth trying a C implementation though. The work-around that's needed for
> array references [= 2* $a(b)] would defeat the caching, so it would be good
> to speed up the parsing if possible. Also I think your caching may be
> equivalent to doing byte-compilation, in which case it may make sense to
> use the framework which already exists for that.
>
> Colin.
>
> On 04/11/2025 01:18, EricT wrote:
>
> that is:
>
>
>
> if {[info exist ::cache($exp)]} {
>
> tailcall ::tcl::unsupported::assemble $::cache($exp)
> }
>
>
>
> (hate gmail!)
>
>
>
>
>
> On Mon, Nov 3, 2025 at 5:17 PM EricT <tw...@gm...> wrote:
>
> and silly of me, it should be:
> if {[info exist ::cache($exp)]} {
> tailcall ::tcl::unsupported::assemble $::cache($exp)
> }
>
>
>
> On Mon, Nov 3, 2025 at 4:50 PM EricT <tw...@gm...> wrote:
>
> With a debug line back in plus the tailcall:
>
>
>
> proc = args {
>
> set exp [join $args]
> if { [info exist ::cache($exp)] } {
> return [tailcall ::tcl::unsupported::assemble $::cache($exp)]
> }
> set tokens [tokenise $exp]
> deb1 "TOKENS = '$tokens'"
> set code [compile $tokens]
> deb1 "GENERATED CODE:\n$code\n"
> puts "exp= |$exp| code= |$code| ifexist: [info exist ::cache($exp)]"
> set ::cache($exp) $code
> uplevel [list ::tcl::unsupported::assemble $code]
> }
>
>
>
> % set a 5
> 5
> % set b 10
> 10
> % = a + b
> exp= |a + b| code= |push a; loadStk; push b; loadStk; add; | ifexist: 0
> 15
> % = a + b
> 15
>
> % time {= a + b} 1000
> 1.73 microseconds per iteration
>
> Faster still!
>
>
>
> I thought the uplevel was needed to be able to get the local variables,
> seems not.
>
> % proc foo arg {set a 5; set b 10; set c [= a+b+arg]}
> % foo 5
> exp= |a+b+arg| code= |push a; loadStk; push b; loadStk; add; push arg;
> loadStk; add; | ifexist: 0
> 20
> % foo 5
> 20
>
>
>
> % proc foo arg {global xxx; set a 5; set b 10; set c [= a+b+arg+xxx]}
>
> % set xxx 100
> 100
> % foo 200
> 315
> % time {foo 200} 10000
> 2.1775 microseconds per iteration
>
>
>
> % parray cache
>
> cache(a + b) = push a; loadStk; push b; loadStk; add;
> cache(a+b+arg) = push a; loadStk; push b; loadStk; add; push arg;
> loadStk; add;
> cache(a+b+arg+xxx) = push a; loadStk; push b; loadStk; add; push arg;
> loadStk; add; push xxx; loadStk; add;
>
>
>
> Very Impressive, great job Colin! Great catch Don!
>
>
>
> Eric
>
>
>
>
>
>
>
>
>
> On Mon, Nov 3, 2025 at 4:22 PM Donald Porter via Tcl-Core <
> tcl...@li...> wrote:
>
> Check what effect replacing [uplevel] with [tailcall] has.
>
>
>
> On Nov 3, 2025, at 7:13 PM, EricT <tw...@gm...> wrote:
>
>
>
> Subject: Your bytecode expression evaluator - impressive results with
> caching!
>
> Hey Colin:
>
> I took a look at your bytecode-based expression evaluator and was
> intrigued by the approach. I made a small modification to add caching and
> the results are really impressive. Here's what I changed:
>
> proc = args {
> set exp [join $args]
> if {[info exist ::cache($exp)]} {
> return [uplevel [list ::tcl::unsupported::assemble $::cache($exp)]]
> }
> set tokens [tokenise $exp]
> deb1 "TOKENS = '$tokens'"
> set code [compile $tokens]
> deb1 "GENERATED CODE:\n$code\n"
> set ::cache($exp) $code
> uplevel [list ::tcl::unsupported::assemble $code]
> }
>
> The cache is just a simple array lookup - one line to store, one line to
> retrieve. But the performance impact is huge:
>
> Performance Tests
>
> Without caching
> % time {= 1 + 2} 1000
> 24.937 microseconds per iteration
>
> With caching
> % time {= 1 + 2} 1000
> 1.8 microseconds per iteration
>
> That's a 13x speedup! The tokenize and parse steps were eating about 92%
> of the execution time.
>
> The Real Magic: Bare Variables + Caching
>
> What really impressed me is how well your bare variable feature synergizes
> with caching:
>
> % set a 5
> 5
> % set b 6
> 6
> % = a + b
> 11
> % time {= a + b} 1000
> 2.079 microseconds per iteration
>
> Now change the variable values
> % set a 10
> 10
> % = a + b
> 16
> % time {= a + b} 1000
> 2.188 microseconds per iteration
>
> The cache entry stays valid even when the variable values change! Why?
> Because the bytecode stores variable names, not values:
>
> push a; loadStk; push b; loadStk; add;
>
> The loadStk instruction does runtime lookup, so:
> - Cache key is stable: "a + b"
> - Works for any values of a and b
> - One cache entry handles all value combinations
>
> Compare this to if we used $-substitution:
>
> = $a + $b # With a=5, b=6 becomes "5 + 6"
> = $a + $b # With a=10, b=6 becomes "10 + 6" - different cache key!
>
> Every value change would create a new cache entry or worse, a cache miss.
>
> Comparison to Other Approaches
>
> Tcl's expr: about 0.40 microseconds
> Direct C evaluator: about 0.53 microseconds
> Your cached approach: about 1.80 microseconds
> Your uncached approach: about 24.9 microseconds
>
> With caching, you're only 3-4x slower than a direct C evaluator.
>
>
> My Assessment
>
> Your design is excellent. The bare variable feature isn't just syntax
> sugar - it's essential for good cache performance. The synergy between:
>
> 1. Bare variables leading to stable cache keys
> 2. Runtime lookup keeping cache hot
> 3. Simple caching providing dramatic speedup
>
> makes this really elegant.
>
> My recommendation: Keep it in Tcl! The implementation is clean,
> performance is excellent (1.8 microseconds is plenty fast), and converting
> to C would add significant complexity for minimal gain (maybe getting to
> about 1.0 microseconds).
>
> The Tcl prototype with caching is actually the right solution here.
> Sometimes the prototype IS the product!
>
> Excellent work on this. The bytecode approach really shines with caching
> enabled.
>
>
>
> On Sun, Nov 2, 2025 at 10:14 AM Colin Macleod via Tcl-Core <
> tcl...@li...> wrote:
>
> Hi again,
>
> I've now made a slightly more serious prototype, see
> https://cmacleod.me.uk/tcl/expr_ng
>
> This is a modified version of the prototype I wrote for tip 676. It's
> still in Tcl, but doesn't use `expr`. It tokenises and parses the input,
> then generates TAL bytecode and uses ::tcl::unsupported::assemble to run
> that. A few examples:
>
> (bin) 100 % set a [= 3.0/4]
> 0.75
> (bin) 101 % set b [= sin(a*10)]
> 0.9379999767747389
> (bin) 102 % set c [= (b-a)*100]
> 18.79999767747389
> (bin) 103 % namespace eval nn {set d [= 10**3]}
> 1000
> (bin) 104 % set e [= a?nn::d:b]
> 1000
> (bin) 105 % = {3 + [pwd]}
> Calc: expected start of expression but found '[pwd]'
> (bin) 106 % = {3 + $q}
> Calc: expected start of expression but found '$q'
> (bin) 107 % = sin (12)
> -0.5365729180004349
>
> (bin) 108 % array set rr {one 1 two 2 three 3}
> (bin) 110 % = a * rr(two)
> Calc: expected operator but found '('
> (bin) 111 % = a * $rr(two)
> 1.5
>
> - You can use $ to get an array value substituted before the `=` code sees
> the expression.
>
> (bin) 112 % string repeat ! [= nn::d / 15]
> !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
>
> Colin.
>
> On 02/11/2025 09:04, Donal Fellows wrote:
>
> Doing the job properly would definitely involve changing the expression
> parser, with my suggested fix being to turn all bare words not otherwise
> recognised as constants or in positions that look like function calls (it's
> a parser with some lookahead) into simple variable reads (NB: C resolves
> such ambiguities within itself differently, but that's one of the nastiest
> parts of the language). We would need to retain $ support for resolving
> ambiguity (e.g., array reads vs function calls; you can't safely inspect
> the interpreter to resolve it at the time of compiling the expression due
> to traces and unknown handlers) as well as compatibility, but that's doable
> as it is a change only in cases that are currently errors.
>
>
>
> Adding assignment is quite a bit trickier, as that needs a new major
> syntax class to describe the left side of the assignment. I suggest
> omitting that from consideration at this stage.
>
>
>
> Donal.
>
>
>
> -------- Original message --------
>
> From: Colin Macleod via Tcl-Core <tcl...@li...>
> <tcl...@li...>
>
> Date: 02/11/2025 08:13 (GMT+00:00)
>
> To: Pietro Cerutti <ga...@ga...> <ga...@ga...>
>
> Cc: tcl...@li..., av...@lo...
>
> Subject: Re: [TCLCORE] Fwd: TIP 672 Implementation Complete - Ready for
> Sponsorship
>
>
>
> Indeed, this toy implementation doesn't handle that:
>
> % = sin (12)
> can't read "sin": no such variable
>
> I'm not sure that's serious, but it could be fixed in a C implementation.
>
> _______________________________________________
> Tcl-Core mailing list
> Tcl...@li...
> https://lists.sourceforge.net/lists/listinfo/tcl-core
>
> _______________________________________________
> Tcl-Core mailing list
> Tcl...@li...
> https://lists.sourceforge.net/lists/listinfo/tcl-core
>
>
>
> _______________________________________________
> Tcl-Core mailing list
> Tcl...@li...
> https://lists.sourceforge.net/lists/listinfo/tcl-core
>
>
>
> _______________________________________________
>
> Tcl-Core mailing list
>
> Tcl...@li...
>
> https://lists.sourceforge.net/lists/listinfo/tcl-core
>
> _______________________________________________
> Tcl-Core mailing list
> Tcl...@li...
> https://lists.sourceforge.net/lists/listinfo/tcl-core
>
> _______________________________________________
> Tcl-Core mailing list
> Tcl...@li...
> https://lists.sourceforge.net/lists/listinfo/tcl-core
>
> _______________________________________________
> Tcl-Core mailing list
> Tcl...@li...
> https://lists.sourceforge.net/lists/listinfo/tcl-core
>
|
|
From: Phillip B. <phi...@um...> - 2025-11-07 21:42:32
|
Hi all, I started a wiki page that tries to summarize the various proposals under discussion: https://wiki.tcl-lang.org/page/A+Better+way+to+do+calculations. Please contribute by adding to and modifying the page with concise descriptions. Let's not try to carry out debates there, but, rather, make it a clear and concise description of the alternatives that we are considering. I have sort of lost track of where we are with this right now. Regarding the last few emails concerning exposing the tcl::unsupported::assemble command, I think the hesitancy there will be with what users do with retrieved assembly code once they have it. Will they store it and expect it to work across Tcl versions? Will they attempt to modify it and expect the modifications to work across Tcl versions? Those are very real reasons for proceeding with caution in that regard. I think we are much better off concluding a useful syntax and going with that in the Tcl core rather than trying to make this a toolkit that allows everyone to solve the underlying problem on their own. Phil |
|
From: EricT <tw...@gm...> - 2025-11-07 22:14:51
|
Hi Phillip:
In going from tcl 8.6 to tcl 9.0 the only things that broke, at least where
Colin's program is concerned, were land and lor, which are no longer
supported. If the bytecode were to have to support those two because they
had been made public, I don't see the big harm. In 8.6 they'd already gone
to a jump based arrangement for short circuiting. So, in 9.x they just
removed these 2 instructions.
That said, I've been able to shave down from 4x to 3x with this simple
optimization. It's interesting how much can go really fast when it's byte
optimized. By avoiding [join] when there's only 1 arg (either by bracing or
taking out the whitespace), I've achieved a 25% improvement. Colin's =
command is now this (also turns out that alias is costly too):
proc : {arg args} {
if { [llength $args] != 0 } {
set arg [join "$arg $args"]
}
if { [info exist ::cache($arg)] } {
# incr ::cachehits($exp)
tailcall ::tcl::unsupported::assemble $::cache($arg)
}
tailcall ::tcl::unsupported::assemble [set ::cache($arg) [compile0
$arg]]
}
interp alias {} = {} : ;# shorthands
I've broken up the compile proc into two parts, compile0 splits multiple
expressions on newline or semicolon. That and adding an assignment operator
took 1/2 day and 40 lines of code. Once more, hat's off to Colin for a
beautifully designed program! Since I'll likely never live to see a tcl 10,
I'm not personally too worried about compatibility, and will just use what
I have now in any programs I write in the future.
I've also begun looking at the assemble command code, and it would appear
that all the needed parts for caching bytecode already exist. The remaining
3x is most certainly that they assemble, execute, and cleanup on every
call. It shouldn't be that difficult to have it save the intermediate
parts. Then these 3 steps could be reduced to one step that would have to
turn a handle into a pointer to the saved bytecode. And that likely is
optimised too since it's such a common Tcl idiom.
It's all quite doable. But I understand the reluctance some will have, but
the payoff could bring a nice performance boost I only thought would be
possible with JIT machine code. And a public API need not expose the entire
assembly language. Also, just having the assemble command not be a
procedure call, and inlined instead, likely would shave off another 1x of
the 3x remaining.
Regards,
Eric
On Fri, Nov 7, 2025 at 1:42 PM Phillip Brooks <phi...@um...> wrote:
> Hi all,
> I started a wiki page that tries to summarize the various proposals under
> discussion:
> https://wiki.tcl-lang.org/page/A+Better+way+to+do+calculations. Please
> contribute by adding to and modifying the page with concise descriptions.
> Let's not try to carry out debates there, but, rather, make it a clear and
> concise description of the alternatives that we are considering. I have
> sort of lost track of where we are with this right now.
>
> Regarding the last few emails concerning exposing the tcl::unsupported::assemble
> command, I think the hesitancy there will be with what users do with
> retrieved assembly code once they have it. Will they store it and expect
> it to work across Tcl versions? Will they attempt to modify it and expect
> the modifications to work across Tcl versions? Those are very real reasons
> for proceeding with caution in that regard. I think we are much better off
> concluding a useful syntax and going with that in the Tcl core rather than
> trying to make this a toolkit that allows everyone to solve the underlying
> problem on their own.
>
> Phil
> _______________________________________________
> Tcl-Core mailing list
> Tcl...@li...
> https://lists.sourceforge.net/lists/listinfo/tcl-core
>
|
|
From: Phillip B. <phi...@um...> - 2025-11-08 00:16:04
|
On Fri, Nov 7, 2025 at 2:14 PM EricT <tw...@gm...> wrote: > Hi Phillip: > > In going from tcl 8.6 to tcl 9.0 the only things that broke, at least > where Colin's program is concerned, were land and lor, which are no longer > supported. If the bytecode were to have to support those two because they > had been made public, I don't see the big harm. In 8.6 they'd already gone > to a jump based arrangement for short circuiting. So, in 9.x they just > removed these 2 instructions. > The harm is that the bytecode is currently allowed to change completely from release to release. This allows for reorganization and optimization that would otherwise be impossible. The way you are using it is, I think, safe since you only ever create and run it in the same session. If opened up for general use, people might try to save it to a file, and then use that on a different release. Others are more expert in this area than I am, though. Phil > |
|
From: EricT <tw...@gm...> - 2025-11-08 01:05:24
|
Philip: Well, maybe it's a good thing that I don't have any tct votes. But now I have some pure tcl, that runs very fast and finally I can put the expr dragon to rest. I have several versions of tcl, and many of Ashok's great tclkit's bundled with twapi. I hope he finds a way to create more, especially for 9.x. I have some utilities that for some forgotten reason still need an older 8.6, and so that's how they launch. If you want to try my latest fork of Colin's masterpiece, you can just tclsh it and then take a look at the bytecode, as it dumps the cache at the end. There are now 140 test cases you can look through as examples of usage. The last 10 also use : as the command name, which I think of as a slimmer alias of = that just seems to look right to my eyes. But = works as well. Here's the very latest: https://github.com/rocketship88/colin-parser Eric On Fri, Nov 7, 2025 at 4:16 PM Phillip Brooks <phi...@um...> wrote: > > > On Fri, Nov 7, 2025 at 2:14 PM EricT <tw...@gm...> wrote: > >> Hi Phillip: >> >> In going from tcl 8.6 to tcl 9.0 the only things that broke, at least >> where Colin's program is concerned, were land and lor, which are no longer >> supported. If the bytecode were to have to support those two because they >> had been made public, I don't see the big harm. In 8.6 they'd already gone >> to a jump based arrangement for short circuiting. So, in 9.x they just >> removed these 2 instructions. >> > > The harm is that the bytecode is currently allowed to change completely > from release to release. This allows for reorganization and optimization > that would otherwise be impossible. The way you are using it is, I think, > safe since you only ever create and run it in the same session. If opened > up for general use, people might try to save it to a file, and then use > that on a different release. Others are more expert in this area than I > am, though. > > Phil > >> _______________________________________________ > Tcl-Core mailing list > Tcl...@li... > https://lists.sourceforge.net/lists/listinfo/tcl-core > |
|
From: Steve L. <st...@di...> - 2025-11-08 01:40:50
|
I am very positive about Colin's approach, especially with the speedups you (Eric) have recommended. In my opinion it offers the best hope of improving the expr command without breaking code or introducing Lots of Interminably Silly Parentheses (see what I did there?). As for bytecode changes, as Phil notes they come with the risk of making the solution release-dependent but I don't think that should be seen as a show-stopper given the value of an improved expr. Just my 2c worth -- Steve On 8 Nov 2025 at 9:06 AM +0800, EricT <tw...@gm...>, wrote: > Philip: > > Well, maybe it's a good thing that I don't have any tct votes. But now I have some pure tcl, that runs very fast and finally I can put the expr dragon to rest. I have several versions of tcl, and many of Ashok's great tclkit's bundled with twapi. I hope he finds a way to create more, especially for 9.x. I have some utilities that for some forgotten reason still need an older 8.6, and so that's how they launch. > > If you want to try my latest fork of Colin's masterpiece, you can just tclsh it and then take a look at the bytecode, as it dumps the cache at the end. There are now 140 test cases you can look through as examples of usage. The last 10 also use : as the command name, which I think of as a slimmer alias of = that just seems to look right to my eyes. But = works as well. > > Here's the very latest: > > https://github.com/rocketship88/colin-parser > > Eric > > > On Fri, Nov 7, 2025 at 4:16 PM Phillip Brooks <phi...@um...> wrote: > > > > > > > On Fri, Nov 7, 2025 at 2:14 PM EricT <tw...@gm...> wrote: > > > > Hi Phillip: > > > > > > > > In going from tcl 8.6 to tcl 9.0 the only things that broke, at least where Colin's program is concerned, were land and lor, which are no longer supported. If the bytecode were to have to support those two because they had been made public, I don't see the big harm. In 8.6 they'd already gone to a jump based arrangement for short circuiting. So, in 9.x they just removed these 2 instructions. > > > > > > The harm is that the bytecode is currently allowed to change completely from release to release. This allows for reorganization and optimization that would otherwise be impossible. The way you are using it is, I think, safe since you only ever create and run it in the same session. If opened up for general use, people might try to save it to a file, and then use that on a different release. Others are more expert in this area than I am, though. > > > > > > Phil > > _______________________________________________ > > Tcl-Core mailing list > > Tcl...@li... > > https://lists.sourceforge.net/lists/listinfo/tcl-core > _______________________________________________ > Tcl-Core mailing list > Tcl...@li... > https://lists.sourceforge.net/lists/listinfo/tcl-core |
|
From: Phillip B. <phi...@um...> - 2025-11-08 01:44:12
|
I would like to see something like this in the core. Then, there is no concern about using the undocumented interface. Phil On Fri, Nov 7, 2025 at 5:40 PM Steve Landers <st...@di...> wrote: > I am very positive about Colin's approach, especially with the speedups > you (Eric) have recommended. In my opinion it offers the best hope of > improving the expr command without breaking code or introducing Lots of > Interminably Silly Parentheses (see what I did there?). > > As for bytecode changes, as Phil notes they come with the risk of making > the solution release-dependent but I don't think that should be seen as a > show-stopper given the value of an improved expr. > > Just my 2c worth > > -- Steve > On 8 Nov 2025 at 9:06 AM +0800, EricT <tw...@gm...>, wrote: > > Philip: > > Well, maybe it's a good thing that I don't have any tct votes. But now I > have some pure tcl, that runs very fast and finally I can put the expr > dragon to rest. I have several versions of tcl, and many of Ashok's great > tclkit's bundled with twapi. I hope he finds a way to create more, > especially for 9.x. I have some utilities that for some forgotten reason > still need an older 8.6, and so that's how they launch. > > If you want to try my latest fork of Colin's masterpiece, you can just > tclsh it and then take a look at the bytecode, as it dumps the cache at the > end. There are now 140 test cases you can look through as examples of > usage. The last 10 also use : as the command name, which I think of as a > slimmer alias of = that just seems to look right to my eyes. But = works as > well. > > Here's the very latest: > > https://github.com/rocketship88/colin-parser > > Eric > > > On Fri, Nov 7, 2025 at 4:16 PM Phillip Brooks <phi...@um...> wrote: > >> >> >> On Fri, Nov 7, 2025 at 2:14 PM EricT <tw...@gm...> wrote: >> >>> Hi Phillip: >>> >>> In going from tcl 8.6 to tcl 9.0 the only things that broke, at least >>> where Colin's program is concerned, were land and lor, which are no longer >>> supported. If the bytecode were to have to support those two because they >>> had been made public, I don't see the big harm. In 8.6 they'd already gone >>> to a jump based arrangement for short circuiting. So, in 9.x they just >>> removed these 2 instructions. >>> >> >> The harm is that the bytecode is currently allowed to change completely >> from release to release. This allows for reorganization and optimization >> that would otherwise be impossible. The way you are using it is, I think, >> safe since you only ever create and run it in the same session. If opened >> up for general use, people might try to save it to a file, and then use >> that on a different release. Others are more expert in this area than I >> am, though. >> >> Phil >> >>> _______________________________________________ >> Tcl-Core mailing list >> Tcl...@li... >> https://lists.sourceforge.net/lists/listinfo/tcl-core >> > _______________________________________________ > Tcl-Core mailing list > Tcl...@li... > https://lists.sourceforge.net/lists/listinfo/tcl-core > > |
|
From: EricT <tw...@gm...> - 2025-11-08 05:56:28
|
HI all,
Well it isn't pretty, but now we have an equivalent test for land/lor,
Took a few go-arounds with gemini to get the logic right, but here's the
replacements that work in both 8.6 and 9.x
foreach {op prec code} {
) 0 -
, 0 -
= 0 -
? 1 -
: 1 -
|| 2 {jumpTrue true_res; jumpFalse pop_A; push 1; jump end_res; label
pop_A; push 0; jump end_res; label true_res; pop; push 1; label end_res}
&& 3 {jumpFalse pop_A; jumpFalse false_res; push 1; jump end_res; label
pop_A; pop; label false_res; push 0; label end_res}
| 4 bitor
^ 5 bitxor
& 6 bitand
......
Not sure why they bothered to remove land/lor, couldn't have saved much by
removing it. Now that's I guess what the developers will not like, that
they can't just remove it if it goes public. But it was after all an easy
fix. The other codes are unlikely to go away any time soon I would think.
And a side by side compare shows that the only other differences, at least
according to the list output by assemble if you give it an invalid one, are
that these are all new to 9.x
dictGetDef
dictPut
dictRemove
isEmpty
jumpTableNum
strge
strgt
strle
strlt
swap
tclooId
Stack machines are kinda fun. And the assembler is actually pretty
friendly, it knew in advance that I was going to have a stack imbalance,
and then the next try gave a stack underrun, but it simply threw an error
with a nice message. Very cool I'd say. They ain't kidding when they say, "
Gemini can make mistakes, so double-check it ". Anyone care to verify
these, though my 146 + 8 more for the logic test cases are all working on
both 8.6 and 9.x.
There is one thing, the code we're generating has to push all
variable names, unlike expr where the compiler is smarter and has locals
that it doesn't have to lookup by name each time. We can't use a peephole
optimiser, like the compiler, so expr will always have that advantage. But
we're in the same ballpark at least.
Eric
On Fri, Nov 7, 2025 at 5:44 PM Phillip Brooks <phi...@um...> wrote:
> I would like to see something like this in the core. Then, there is no
> concern about using the undocumented interface.
>
> Phil
>
> On Fri, Nov 7, 2025 at 5:40 PM Steve Landers <st...@di...>
> wrote:
>
>> I am very positive about Colin's approach, especially with the speedups
>> you (Eric) have recommended. In my opinion it offers the best hope of
>> improving the expr command without breaking code or introducing Lots of
>> Interminably Silly Parentheses (see what I did there?).
>>
>> As for bytecode changes, as Phil notes they come with the risk of making
>> the solution release-dependent but I don't think that should be seen as a
>> show-stopper given the value of an improved expr.
>>
>> Just my 2c worth
>>
>> -- Steve
>> On 8 Nov 2025 at 9:06 AM +0800, EricT <tw...@gm...>, wrote:
>>
>> Philip:
>>
>> Well, maybe it's a good thing that I don't have any tct votes. But now I
>> have some pure tcl, that runs very fast and finally I can put the expr
>> dragon to rest. I have several versions of tcl, and many of Ashok's great
>> tclkit's bundled with twapi. I hope he finds a way to create more,
>> especially for 9.x. I have some utilities that for some forgotten reason
>> still need an older 8.6, and so that's how they launch.
>>
>> If you want to try my latest fork of Colin's masterpiece, you can just
>> tclsh it and then take a look at the bytecode, as it dumps the cache at the
>> end. There are now 140 test cases you can look through as examples of
>> usage. The last 10 also use : as the command name, which I think of as a
>> slimmer alias of = that just seems to look right to my eyes. But = works as
>> well.
>>
>> Here's the very latest:
>>
>> https://github.com/rocketship88/colin-parser
>>
>> Eric
>>
>>
>> On Fri, Nov 7, 2025 at 4:16 PM Phillip Brooks <phi...@um...> wrote:
>>
>>>
>>>
>>> On Fri, Nov 7, 2025 at 2:14 PM EricT <tw...@gm...> wrote:
>>>
>>>> Hi Phillip:
>>>>
>>>> In going from tcl 8.6 to tcl 9.0 the only things that broke, at least
>>>> where Colin's program is concerned, were land and lor, which are no longer
>>>> supported. If the bytecode were to have to support those two because they
>>>> had been made public, I don't see the big harm. In 8.6 they'd already gone
>>>> to a jump based arrangement for short circuiting. So, in 9.x they just
>>>> removed these 2 instructions.
>>>>
>>>
>>> The harm is that the bytecode is currently allowed to change completely
>>> from release to release. This allows for reorganization and optimization
>>> that would otherwise be impossible. The way you are using it is, I think,
>>> safe since you only ever create and run it in the same session. If opened
>>> up for general use, people might try to save it to a file, and then use
>>> that on a different release. Others are more expert in this area than I
>>> am, though.
>>>
>>> Phil
>>>
>>>> _______________________________________________
>>> Tcl-Core mailing list
>>> Tcl...@li...
>>> https://lists.sourceforge.net/lists/listinfo/tcl-core
>>>
>> _______________________________________________
>> Tcl-Core mailing list
>> Tcl...@li...
>> https://lists.sourceforge.net/lists/listinfo/tcl-core
>>
>> _______________________________________________
> Tcl-Core mailing list
> Tcl...@li...
> https://lists.sourceforge.net/lists/listinfo/tcl-core
>
|
|
From: Phillip B. <phi...@um...> - 2025-11-08 05:59:12
|
I don’t understand the motivation for this.
Phil
On Fri, Nov 7, 2025 at 9:56 PM EricT <tw...@gm...> wrote:
> HI all,
>
> Well it isn't pretty, but now we have an equivalent test for land/lor,
>
> Took a few go-arounds with gemini to get the logic right, but here's the
> replacements that work in both 8.6 and 9.x
>
> foreach {op prec code} {
> ) 0 -
> , 0 -
> = 0 -
> ? 1 -
> : 1 -
> || 2 {jumpTrue true_res; jumpFalse pop_A; push 1; jump end_res; label
> pop_A; push 0; jump end_res; label true_res; pop; push 1; label end_res}
> && 3 {jumpFalse pop_A; jumpFalse false_res; push 1; jump end_res;
> label pop_A; pop; label false_res; push 0; label end_res}
> | 4 bitor
> ^ 5 bitxor
> & 6 bitand
> ......
>
>
> Not sure why they bothered to remove land/lor, couldn't have saved much by
> removing it. Now that's I guess what the developers will not like, that
> they can't just remove it if it goes public. But it was after all an easy
> fix. The other codes are unlikely to go away any time soon I would think.
> And a side by side compare shows that the only other differences, at least
> according to the list output by assemble if you give it an invalid one, are
> that these are all new to 9.x
>
> dictGetDef
> dictPut
> dictRemove
> isEmpty
> jumpTableNum
> strge
> strgt
> strle
> strlt
> swap
> tclooId
>
> Stack machines are kinda fun. And the assembler is actually pretty
> friendly, it knew in advance that I was going to have a stack imbalance,
> and then the next try gave a stack underrun, but it simply threw an error
> with a nice message. Very cool I'd say. They ain't kidding when they say, "
> Gemini can make mistakes, so double-check it ". Anyone care to verify
> these, though my 146 + 8 more for the logic test cases are all working on
> both 8.6 and 9.x.
>
> There is one thing, the code we're generating has to push all
> variable names, unlike expr where the compiler is smarter and has locals
> that it doesn't have to lookup by name each time. We can't use a peephole
> optimiser, like the compiler, so expr will always have that advantage. But
> we're in the same ballpark at least.
>
> Eric
>
>
> On Fri, Nov 7, 2025 at 5:44 PM Phillip Brooks <phi...@um...> wrote:
>
>> I would like to see something like this in the core. Then, there is no
>> concern about using the undocumented interface.
>>
>> Phil
>>
>> On Fri, Nov 7, 2025 at 5:40 PM Steve Landers <st...@di...>
>> wrote:
>>
>>> I am very positive about Colin's approach, especially with the speedups
>>> you (Eric) have recommended. In my opinion it offers the best hope of
>>> improving the expr command without breaking code or introducing Lots of
>>> Interminably Silly Parentheses (see what I did there?).
>>>
>>> As for bytecode changes, as Phil notes they come with the risk of
>>> making the solution release-dependent but I don't think that should be seen
>>> as a show-stopper given the value of an improved expr.
>>>
>>> Just my 2c worth
>>>
>>> -- Steve
>>> On 8 Nov 2025 at 9:06 AM +0800, EricT <tw...@gm...>, wrote:
>>>
>>> Philip:
>>>
>>> Well, maybe it's a good thing that I don't have any tct votes. But now I
>>> have some pure tcl, that runs very fast and finally I can put the expr
>>> dragon to rest. I have several versions of tcl, and many of Ashok's great
>>> tclkit's bundled with twapi. I hope he finds a way to create more,
>>> especially for 9.x. I have some utilities that for some forgotten reason
>>> still need an older 8.6, and so that's how they launch.
>>>
>>> If you want to try my latest fork of Colin's masterpiece, you can just
>>> tclsh it and then take a look at the bytecode, as it dumps the cache at the
>>> end. There are now 140 test cases you can look through as examples of
>>> usage. The last 10 also use : as the command name, which I think of as a
>>> slimmer alias of = that just seems to look right to my eyes. But = works as
>>> well.
>>>
>>> Here's the very latest:
>>>
>>> https://github.com/rocketship88/colin-parser
>>>
>>> Eric
>>>
>>>
>>> On Fri, Nov 7, 2025 at 4:16 PM Phillip Brooks <phi...@um...>
>>> wrote:
>>>
>>>>
>>>>
>>>> On Fri, Nov 7, 2025 at 2:14 PM EricT <tw...@gm...> wrote:
>>>>
>>>>> Hi Phillip:
>>>>>
>>>>> In going from tcl 8.6 to tcl 9.0 the only things that broke, at least
>>>>> where Colin's program is concerned, were land and lor, which are no longer
>>>>> supported. If the bytecode were to have to support those two because they
>>>>> had been made public, I don't see the big harm. In 8.6 they'd already gone
>>>>> to a jump based arrangement for short circuiting. So, in 9.x they just
>>>>> removed these 2 instructions.
>>>>>
>>>>
>>>> The harm is that the bytecode is currently allowed to change completely
>>>> from release to release. This allows for reorganization and optimization
>>>> that would otherwise be impossible. The way you are using it is, I think,
>>>> safe since you only ever create and run it in the same session. If opened
>>>> up for general use, people might try to save it to a file, and then use
>>>> that on a different release. Others are more expert in this area than I
>>>> am, though.
>>>>
>>>> Phil
>>>>
>>>>> _______________________________________________
>>>> Tcl-Core mailing list
>>>> Tcl...@li...
>>>> https://lists.sourceforge.net/lists/listinfo/tcl-core
>>>>
>>> _______________________________________________
>>> Tcl-Core mailing list
>>> Tcl...@li...
>>> https://lists.sourceforge.net/lists/listinfo/tcl-core
>>>
>>> _______________________________________________
>> Tcl-Core mailing list
>> Tcl...@li...
>> https://lists.sourceforge.net/lists/listinfo/tcl-core
>>
>
|
|
From: Colin M. <col...@ya...> - 2025-11-08 08:15:39
|
I think the reason for not using the `land` and `lor` opcodes is to get
lazy evaluation. E.g. with [expr {this || [do_that]}] if `this` is
true `do_that` does not get executed. This would not work with simple
`land` and `lor` because they operate after both operands have been
computed. This distinction doesn't matter for [=] because it doesn't do
its own command substitution. I already had to write similar code for
the ternary operator ?: .
Colin.
On 08/11/2025 05:58, Phillip Brooks wrote:
> I don’t understand the motivation for this.
>
> Phil
>
> On Fri, Nov 7, 2025 at 9:56 PM EricT <tw...@gm...> wrote:
>
> HI all,
>
> Well it isn't pretty, but now we have an equivalent test for
> land/lor,
>
> Took a few go-arounds with gemini to get the logic right, but
> here's the replacements that work in both 8.6 and 9.x
>
> foreach {op prec code} {
> ) 0 -
> , 0 -
> = 0 -
> ? 1 -
> : 1 -
> || 2 {jumpTrue true_res; jumpFalse pop_A; push 1; jump
> end_res; label pop_A; push 0; jump end_res; label true_res; pop;
> push 1; label end_res}
> && 3 {jumpFalse pop_A; jumpFalse false_res; push 1; jump
> end_res; label pop_A; pop; label false_res; push 0; label end_res}
> | 4 bitor
> ^ 5 bitxor
> & 6 bitand
> ......
>
>
> Not sure why they bothered to remove land/lor, couldn't have saved
> much by removing it. Now that's I guess what the developers will
> not like, that they can't just remove it if it goes public. But it
> was after all an easy fix. The other codes are unlikely to go away
> any time soon I would think. And a side by side compare shows that
> the only other differences, at least according to the list output
> by assemble if you give it an invalid one, are that these are all
> new to 9.x
>
> dictGetDef
> dictPut
> dictRemove
> isEmpty
> jumpTableNum
> strge
> strgt
> strle
> strlt
> swap
> tclooId
>
> Stack machines are kinda fun. And the assembler is actually pretty
> friendly, it knew in advance that I was going to have a stack
> imbalance, and then the next try gave a stack underrun, but it
> simply threw an error with a nice message. Very cool I'd say. They
> ain't kidding when they say, " Gemini can make mistakes, so
> double-check it ". Anyone care to verify these, though my 146 + 8
> more for the logic test cases are all working on both 8.6 and 9.x.
>
> There is one thing, the code we're generating has to push all
> variable names, unlike expr where the compiler is smarter and has
> locals that it doesn't have to lookup by name each time. We can't
> use a peephole optimiser, like the compiler, so expr will always
> have that advantage. But we're in the same ballpark at least.
>
> Eric
>
>
> On Fri, Nov 7, 2025 at 5:44 PM Phillip Brooks <phi...@um...>
> wrote:
>
> I would like to see something like this in the core. Then,
> there is no concern about using the undocumented interface.
>
> Phil
>
> On Fri, Nov 7, 2025 at 5:40 PM Steve Landers
> <st...@di...> wrote:
>
> I am very positive about Colin's approach, especially with
> the speedups you (Eric) have recommended. In my opinion
> it offers the best hope of improving the expr command
> without breaking code or introducing Lots of Interminably
> Silly Parentheses (see what I did there?).
>
> As for bytecode changes, as Phil notes they come with the
> risk of making the solution release-dependent but I don't
> think that should be seen as a show-stopper given the
> value of an improved expr.
>
> Just my 2c worth
>
> -- Steve
> On 8 Nov 2025 at 9:06 AM +0800, EricT <tw...@gm...>,
> wrote:
>> Philip:
>>
>> Well, maybe it's a good thing that I don't have any tct
>> votes. But now I have some pure tcl, that runs very fast
>> and finally I can put the expr dragon to rest. I have
>> several versions of tcl, and many of Ashok's great
>> tclkit's bundled with twapi. I hope he finds a way to
>> create more, especially for 9.x. I have some utilities
>> that for some forgotten reason still need an older 8.6,
>> and so that's how they launch.
>>
>> If you want to try my latest fork of Colin's masterpiece,
>> you can just tclsh it and then take a look at the
>> bytecode, as it dumps the cache at the end. There are now
>> 140 test cases you can look through as examples of
>> usage. The last 10 also use : as the command name, which
>> I think of as a slimmer alias of = that just seems to
>> look right to my eyes. But = works as well.
>>
>> Here's the very latest:
>>
>> https://github.com/rocketship88/colin-parser
>>
>> Eric
>>
>>
>> On Fri, Nov 7, 2025 at 4:16 PM Phillip Brooks
>> <phi...@um...> wrote:
>>
>>
>>
>> On Fri, Nov 7, 2025 at 2:14 PM EricT
>> <tw...@gm...> wrote:
>>
>> Hi Phillip:
>>
>> In going from tcl 8.6 to tcl 9.0 the only things
>> that broke, at least where Colin's program is
>> concerned, were land and lor, which are no longer
>> supported. If the bytecode were to have to
>> support those two because they had been made
>> public, I don't see the big harm. In 8.6 they'd
>> already gone to a jump based arrangement for
>> short circuiting. So, in 9.x they just removed
>> these 2 instructions.
>>
>>
>> The harm is that the bytecode is currently allowed to
>> change completely from release to release. This
>> allows for reorganization and optimization that would
>> otherwise be impossible. The way you are using it
>> is, I think, safe since you only ever create and run
>> it in the same session. If opened up for general
>> use, people might try to save it to a file, and then
>> use that on a different release. Others are more
>> expert in this area than I am, though.
>>
>> Phil
>>
>> _______________________________________________
>> Tcl-Core mailing list
>> Tcl...@li...
>> https://lists.sourceforge.net/lists/listinfo/tcl-core
>>
>> _______________________________________________
>> Tcl-Core mailing list
>> Tcl...@li...
>> https://lists.sourceforge.net/lists/listinfo/tcl-core
>
> _______________________________________________
> Tcl-Core mailing list
> Tcl...@li...
> https://lists.sourceforge.net/lists/listinfo/tcl-core
>
>
>
> _______________________________________________
> Tcl-Core mailing list
> Tcl...@li...
> https://lists.sourceforge.net/lists/listinfo/tcl-core |
|
From: Colin M. <col...@ya...> - 2025-11-08 08:23:47
|
Doh, of course the expr example should be [expr {$this || [do_that]}]
On 08/11/2025 08:15, Colin Macleod via Tcl-Core wrote:
>
> I think the reason for not using the `land` and `lor` opcodes is to
> get lazy evaluation. E.g. with [expr {this || [do_that]}] if `this`
> is true `do_that` does not get executed. This would not work with
> simple `land` and `lor` because they operate after both operands have
> been computed. This distinction doesn't matter for [=] because it
> doesn't do its own command substitution. I already had to write
> similar code for the ternary operator ?: .
>
> Colin.
>
> On 08/11/2025 05:58, Phillip Brooks wrote:
>> I don’t understand the motivation for this.
>>
>> Phil
>>
>> On Fri, Nov 7, 2025 at 9:56 PM EricT <tw...@gm...> wrote:
>>
>> HI all,
>>
>> Well it isn't pretty, but now we have an equivalent test for
>> land/lor,
>>
>> Took a few go-arounds with gemini to get the logic right, but
>> here's the replacements that work in both 8.6 and 9.x
>>
>> foreach {op prec code} {
>> ) 0 -
>> , 0 -
>> = 0 -
>> ? 1 -
>> : 1 -
>> || 2 {jumpTrue true_res; jumpFalse pop_A; push 1; jump
>> end_res; label pop_A; push 0; jump end_res; label true_res; pop;
>> push 1; label end_res}
>> && 3 {jumpFalse pop_A; jumpFalse false_res; push 1; jump
>> end_res; label pop_A; pop; label false_res; push 0; label end_res}
>> | 4 bitor
>> ^ 5 bitxor
>> & 6 bitand
>> ......
>>
>>
>> Not sure why they bothered to remove land/lor, couldn't have
>> saved much by removing it. Now that's I guess what the developers
>> will not like, that they can't just remove it if it goes public.
>> But it was after all an easy fix. The other codes are unlikely to
>> go away any time soon I would think. And a side by side compare
>> shows that the only other differences, at least according to the
>> list output by assemble if you give it an invalid one, are that
>> these are all new to 9.x
>>
>> dictGetDef
>> dictPut
>> dictRemove
>> isEmpty
>> jumpTableNum
>> strge
>> strgt
>> strle
>> strlt
>> swap
>> tclooId
>>
>> Stack machines are kinda fun. And the assembler is actually
>> pretty friendly, it knew in advance that I was going to have a
>> stack imbalance, and then the next try gave a stack underrun, but
>> it simply threw an error with a nice message. Very cool I'd say.
>> They ain't kidding when they say, " Gemini can make mistakes, so
>> double-check it ". Anyone care to verify these, though my 146 + 8
>> more for the logic test cases are all working on both 8.6 and 9.x.
>>
>> There is one thing, the code we're generating has to push all
>> variable names, unlike expr where the compiler is smarter and has
>> locals that it doesn't have to lookup by name each time. We can't
>> use a peephole optimiser, like the compiler, so expr will always
>> have that advantage. But we're in the same ballpark at least.
>>
>> Eric
>>
>>
>> On Fri, Nov 7, 2025 at 5:44 PM Phillip Brooks
>> <phi...@um...> wrote:
>>
>> I would like to see something like this in the core. Then,
>> there is no concern about using the undocumented interface.
>>
>> Phil
>>
>> On Fri, Nov 7, 2025 at 5:40 PM Steve Landers
>> <st...@di...> wrote:
>>
>> I am very positive about Colin's approach, especially
>> with the speedups you (Eric) have recommended. In my
>> opinion it offers the best hope of improving the expr
>> command without breaking code or introducing Lots of
>> Interminably Silly Parentheses (see what I did there?).
>>
>> As for bytecode changes, as Phil notes they come with
>> the risk of making the solution release-dependent but I
>> don't think that should be seen as a show-stopper given
>> the value of an improved expr.
>>
>> Just my 2c worth
>>
>> -- Steve
>> On 8 Nov 2025 at 9:06 AM +0800, EricT <tw...@gm...>,
>> wrote:
>>> Philip:
>>>
>>> Well, maybe it's a good thing that I don't have any tct
>>> votes. But now I have some pure tcl, that runs very fast
>>> and finally I can put the expr dragon to rest. I have
>>> several versions of tcl, and many of Ashok's great
>>> tclkit's bundled with twapi. I hope he finds a way to
>>> create more, especially for 9.x. I have some utilities
>>> that for some forgotten reason still need an older 8.6,
>>> and so that's how they launch.
>>>
>>> If you want to try my latest fork of Colin's
>>> masterpiece, you can just tclsh it and then take a look
>>> at the bytecode, as it dumps the cache at the end. There
>>> are now 140 test cases you can look through as examples
>>> of usage. The last 10 also use : as the command name,
>>> which I think of as a slimmer alias of = that just seems
>>> to look right to my eyes. But = works as well.
>>>
>>> Here's the very latest:
>>>
>>> https://github.com/rocketship88/colin-parser
>>>
>>> Eric
>>>
>>>
>>> On Fri, Nov 7, 2025 at 4:16 PM Phillip Brooks
>>> <phi...@um...> wrote:
>>>
>>>
>>>
>>> On Fri, Nov 7, 2025 at 2:14 PM EricT
>>> <tw...@gm...> wrote:
>>>
>>> Hi Phillip:
>>>
>>> In going from tcl 8.6 to tcl 9.0 the only things
>>> that broke, at least where Colin's program is
>>> concerned, were land and lor, which are no
>>> longer supported. If the bytecode were to have
>>> to support those two because they had been made
>>> public, I don't see the big harm. In 8.6 they'd
>>> already gone to a jump based arrangement for
>>> short circuiting. So, in 9.x they just removed
>>> these 2 instructions.
>>>
>>>
>>> The harm is that the bytecode is currently allowed
>>> to change completely from release to release. This
>>> allows for reorganization and optimization that
>>> would otherwise be impossible. The way you are
>>> using it is, I think, safe since you only ever
>>> create and run it in the same session. If opened up
>>> for general use, people might try to save it to a
>>> file, and then use that on a different release.
>>> Others are more expert in this area than I am, though.
>>>
>>> Phil
>>>
>>> _______________________________________________
>>> Tcl-Core mailing list
>>> Tcl...@li...
>>> https://lists.sourceforge.net/lists/listinfo/tcl-core
>>>
>>> _______________________________________________
>>> Tcl-Core mailing list
>>> Tcl...@li...
>>> https://lists.sourceforge.net/lists/listinfo/tcl-core
>>
>> _______________________________________________
>> Tcl-Core mailing list
>> Tcl...@li...
>> https://lists.sourceforge.net/lists/listinfo/tcl-core
>>
>>
>>
>> _______________________________________________
>> Tcl-Core mailing list
>> Tcl...@li...
>> https://lists.sourceforge.net/lists/listinfo/tcl-core
>
>
> _______________________________________________
> Tcl-Core mailing list
> Tcl...@li...
> https://lists.sourceforge.net/lists/listinfo/tcl-core |
|
From: Colin M. <col...@ya...> - 2025-11-08 08:45:30
|
I haven't tested this, but a simpler alternative to `lor` could be
`bitor; not; not` .
Colin.
On 08/11/2025 05:56, EricT wrote:
> HI all,
>
> Well it isn't pretty, but now we have an equivalent test for land/lor,
>
> Took a few go-arounds with gemini to get the logic right, but here's
> the replacements that work in both 8.6 and 9.x
>
> foreach {op prec code} {
> ) 0 -
> , 0 -
> = 0 -
> ? 1 -
> : 1 -
> || 2 {jumpTrue true_res; jumpFalse pop_A; push 1; jump end_res;
> label pop_A; push 0; jump end_res; label true_res; pop; push 1; label
> end_res}
> && 3 {jumpFalse pop_A; jumpFalse false_res; push 1; jump end_res;
> label pop_A; pop; label false_res; push 0; label end_res}
> | 4 bitor
> ^ 5 bitxor
> & 6 bitand
> ......
>
>
> Not sure why they bothered to remove land/lor, couldn't have saved
> much by removing it. Now that's I guess what the developers will not
> like, that they can't just remove it if it goes public. But it was
> after all an easy fix. The other codes are unlikely to go away any
> time soon I would think. And a side by side compare shows that the
> only other differences, at least according to the list output by
> assemble if you give it an invalid one, are that these are all new to 9.x
>
> dictGetDef
> dictPut
> dictRemove
> isEmpty
> jumpTableNum
> strge
> strgt
> strle
> strlt
> swap
> tclooId
>
> Stack machines are kinda fun. And the assembler is actually pretty
> friendly, it knew in advance that I was going to have a stack
> imbalance, and then the next try gave a stack underrun, but it simply
> threw an error with a nice message. Very cool I'd say. They ain't
> kidding when they say, " Gemini can make mistakes, so double-check it
> ". Anyone care to verify these, though my 146 + 8 more for the logic
> test cases are all working on both 8.6 and 9.x.
>
> There is one thing, the code we're generating has to push all
> variable names, unlike expr where the compiler is smarter and has
> locals that it doesn't have to lookup by name each time. We can't use
> a peephole optimiser, like the compiler, so expr will always have that
> advantage. But we're in the same ballpark at least.
>
> Eric
>
>
> On Fri, Nov 7, 2025 at 5:44 PM Phillip Brooks <phi...@um...> wrote:
>
> I would like to see something like this in the core. Then, there
> is no concern about using the undocumented interface.
>
> Phil
>
> On Fri, Nov 7, 2025 at 5:40 PM Steve Landers
> <st...@di...> wrote:
>
> I am very positive about Colin's approach, especially with the
> speedups you (Eric) have recommended. In my opinion it offers
> the best hope of improving the expr command without breaking
> code or introducing Lots of Interminably Silly Parentheses
> (see what I did there?).
>
> As for bytecode changes, as Phil notes they come with the
> risk of making the solution release-dependent but I don't
> think that should be seen as a show-stopper given the value of
> an improved expr.
>
> Just my 2c worth
>
> -- Steve
> On 8 Nov 2025 at 9:06 AM +0800, EricT <tw...@gm...>, wrote:
>> Philip:
>>
>> Well, maybe it's a good thing that I don't have any tct
>> votes. But now I have some pure tcl, that runs very fast and
>> finally I can put the expr dragon to rest. I have several
>> versions of tcl, and many of Ashok's great
>> tclkit's bundled with twapi. I hope he finds a way to create
>> more, especially for 9.x. I have some utilities that for some
>> forgotten reason still need an older 8.6, and so that's how
>> they launch.
>>
>> If you want to try my latest fork of Colin's masterpiece, you
>> can just tclsh it and then take a look at the bytecode, as it
>> dumps the cache at the end. There are now 140 test cases you
>> can look through as examples of usage. The last 10 also use
>> : as the command name, which I think of as a slimmer alias of
>> = that just seems to look right to my eyes. But = works as well.
>>
>> Here's the very latest:
>>
>> https://github.com/rocketship88/colin-parser
>>
>> Eric
>>
>>
>> On Fri, Nov 7, 2025 at 4:16 PM Phillip Brooks
>> <phi...@um...> wrote:
>>
>>
>>
>> On Fri, Nov 7, 2025 at 2:14 PM EricT <tw...@gm...>
>> wrote:
>>
>> Hi Phillip:
>>
>> In going from tcl 8.6 to tcl 9.0 the only things that
>> broke, at least where Colin's program is concerned,
>> were land and lor, which are no longer supported. If
>> the bytecode were to have to support those two
>> because they had been made public, I don't see the
>> big harm. In 8.6 they'd already gone to a jump based
>> arrangement for short circuiting. So, in 9.x they
>> just removed these 2 instructions.
>>
>>
>> The harm is that the bytecode is currently allowed to
>> change completely from release to release. This allows
>> for reorganization and optimization that would otherwise
>> be impossible. The way you are using it is, I think,
>> safe since you only ever create and run it in the same
>> session. If opened up for general use, people might try
>> to save it to a file, and then use that on a different
>> release. Others are more expert in this area than I am,
>> though.
>>
>> Phil
>>
>> _______________________________________________
>> Tcl-Core mailing list
>> Tcl...@li...
>> https://lists.sourceforge.net/lists/listinfo/tcl-core
>>
>> _______________________________________________
>> Tcl-Core mailing list
>> Tcl...@li...
>> https://lists.sourceforge.net/lists/listinfo/tcl-core
>
> _______________________________________________
> Tcl-Core mailing list
> Tcl...@li...
> https://lists.sourceforge.net/lists/listinfo/tcl-core
>
>
>
> _______________________________________________
> Tcl-Core mailing list
> Tcl...@li...
> https://lists.sourceforge.net/lists/listinfo/tcl-core |
|
From: EricT <tw...@gm...> - 2025-11-08 09:16:22
|
Hi Colin,
I know you didn't want to allow for any booleans, but I still am allowing
mine to use exactly "true" and "false" and with bitor
(this is 8.6)
% tcl::unsupported::disassemble script {expr {1 | $a}}
Command 1: "expr {1 | $a}"
(0) push1 0 # "1"
(2) push1 1 # "a"
(4) loadStk
(5) bitor
(6) done
% set a false
false
% expr {1 | $a}
can't use non-numeric string as operand of "|"
% tcl::unsupported::disassemble script {expr {1 || $a}}
Command 1: "expr {1 || $a}"
(0) push1 0 # "1"
(2) jumpTrue1 +11 # pc 13
(4) push1 1 # "a"
(6) loadStk
(7) jumpTrue1 +6 # pc 13
(9) push1 2 # "0"
(11) jump1 +4 # pc 15
(13) push1 0 # "1"
(15) done
% expr {1 || $a}
1
So, even in 8.6 they're doing the short circuit with jumps. I guess the
jumpFalse and jumpTrue are more tcl like. But this operator as a true/false
test is probably not used that much in calculations, so likely not a
big deal, but at least it doesn't throw an error anymore.
Thanks,
Eric
On Sat, Nov 8, 2025 at 12:45 AM Colin Macleod via Tcl-Core <
tcl...@li...> wrote:
> I haven't tested this, but a simpler alternative to `lor` could be `bitor;
> not; not` .
>
> Colin.
> On 08/11/2025 05:56, EricT wrote:
>
> HI all,
>
> Well it isn't pretty, but now we have an equivalent test for land/lor,
>
> Took a few go-arounds with gemini to get the logic right, but here's the
> replacements that work in both 8.6 and 9.x
>
> foreach {op prec code} {
> ) 0 -
> , 0 -
> = 0 -
> ? 1 -
> : 1 -
> || 2 {jumpTrue true_res; jumpFalse pop_A; push 1; jump end_res; label
> pop_A; push 0; jump end_res; label true_res; pop; push 1; label end_res}
> && 3 {jumpFalse pop_A; jumpFalse false_res; push 1; jump end_res;
> label pop_A; pop; label false_res; push 0; label end_res}
> | 4 bitor
> ^ 5 bitxor
> & 6 bitand
> ......
>
>
> Not sure why they bothered to remove land/lor, couldn't have saved much by
> removing it. Now that's I guess what the developers will not like, that
> they can't just remove it if it goes public. But it was after all an easy
> fix. The other codes are unlikely to go away any time soon I would think.
> And a side by side compare shows that the only other differences, at least
> according to the list output by assemble if you give it an invalid one, are
> that these are all new to 9.x
>
> dictGetDef
> dictPut
> dictRemove
> isEmpty
> jumpTableNum
> strge
> strgt
> strle
> strlt
> swap
> tclooId
>
> Stack machines are kinda fun. And the assembler is actually pretty
> friendly, it knew in advance that I was going to have a stack imbalance,
> and then the next try gave a stack underrun, but it simply threw an error
> with a nice message. Very cool I'd say. They ain't kidding when they say, "
> Gemini can make mistakes, so double-check it ". Anyone care to verify
> these, though my 146 + 8 more for the logic test cases are all working on
> both 8.6 and 9.x.
>
> There is one thing, the code we're generating has to push all
> variable names, unlike expr where the compiler is smarter and has locals
> that it doesn't have to lookup by name each time. We can't use a peephole
> optimiser, like the compiler, so expr will always have that advantage. But
> we're in the same ballpark at least.
>
> Eric
>
>
> On Fri, Nov 7, 2025 at 5:44 PM Phillip Brooks <phi...@um...> wrote:
>
>> I would like to see something like this in the core. Then, there is no
>> concern about using the undocumented interface.
>>
>> Phil
>>
>> On Fri, Nov 7, 2025 at 5:40 PM Steve Landers <st...@di...>
>> wrote:
>>
>>> I am very positive about Colin's approach, especially with the speedups
>>> you (Eric) have recommended. In my opinion it offers the best hope of
>>> improving the expr command without breaking code or introducing Lots of
>>> Interminably Silly Parentheses (see what I did there?).
>>>
>>> As for bytecode changes, as Phil notes they come with the risk of
>>> making the solution release-dependent but I don't think that should be seen
>>> as a show-stopper given the value of an improved expr.
>>>
>>> Just my 2c worth
>>>
>>> -- Steve
>>> On 8 Nov 2025 at 9:06 AM +0800, EricT <tw...@gm...>, wrote:
>>>
>>> Philip:
>>>
>>> Well, maybe it's a good thing that I don't have any tct votes. But now I
>>> have some pure tcl, that runs very fast and finally I can put the expr
>>> dragon to rest. I have several versions of tcl, and many of Ashok's great
>>> tclkit's bundled with twapi. I hope he finds a way to create more,
>>> especially for 9.x. I have some utilities that for some forgotten reason
>>> still need an older 8.6, and so that's how they launch.
>>>
>>> If you want to try my latest fork of Colin's masterpiece, you can just
>>> tclsh it and then take a look at the bytecode, as it dumps the cache at the
>>> end. There are now 140 test cases you can look through as examples of
>>> usage. The last 10 also use : as the command name, which I think of as a
>>> slimmer alias of = that just seems to look right to my eyes. But = works as
>>> well.
>>>
>>> Here's the very latest:
>>>
>>> https://github.com/rocketship88/colin-parser
>>>
>>> Eric
>>>
>>>
>>> On Fri, Nov 7, 2025 at 4:16 PM Phillip Brooks <phi...@um...>
>>> wrote:
>>>
>>>>
>>>>
>>>> On Fri, Nov 7, 2025 at 2:14 PM EricT <tw...@gm...> wrote:
>>>>
>>>>> Hi Phillip:
>>>>>
>>>>> In going from tcl 8.6 to tcl 9.0 the only things that broke, at least
>>>>> where Colin's program is concerned, were land and lor, which are no longer
>>>>> supported. If the bytecode were to have to support those two because they
>>>>> had been made public, I don't see the big harm. In 8.6 they'd already gone
>>>>> to a jump based arrangement for short circuiting. So, in 9.x they just
>>>>> removed these 2 instructions.
>>>>>
>>>>
>>>> The harm is that the bytecode is currently allowed to change completely
>>>> from release to release. This allows for reorganization and optimization
>>>> that would otherwise be impossible. The way you are using it is, I think,
>>>> safe since you only ever create and run it in the same session. If opened
>>>> up for general use, people might try to save it to a file, and then use
>>>> that on a different release. Others are more expert in this area than I
>>>> am, though.
>>>>
>>>> Phil
>>>>
>>>>> _______________________________________________
>>>> Tcl-Core mailing list
>>>> Tcl...@li...
>>>> https://lists.sourceforge.net/lists/listinfo/tcl-core
>>>>
>>> _______________________________________________
>>> Tcl-Core mailing list
>>> Tcl...@li...
>>> https://lists.sourceforge.net/lists/listinfo/tcl-core
>>>
>>> _______________________________________________
>> Tcl-Core mailing list
>> Tcl...@li...
>> https://lists.sourceforge.net/lists/listinfo/tcl-core
>>
>
>
> _______________________________________________
> Tcl-Core mailing lis...@li...://lists.sourceforge.net/lists/listinfo/tcl-core
>
> _______________________________________________
> Tcl-Core mailing list
> Tcl...@li...
> https://lists.sourceforge.net/lists/listinfo/tcl-core
>
|
|
From: Colin M. <col...@ya...> - 2025-11-08 15:03:33
|
Similarly, if we restrict to numeric operands, an alternative to `land`
would be `not; swap; not; bitor; not` . However when I try this
::tcl::unsupported::assemble tells me `swap` is not a valid instruction,
although it is listed in generic/tclAssembly.c 😮
Colin.
On 08/11/2025 08:45, Colin Macleod via Tcl-Core wrote:
>
> I haven't tested this, but a simpler alternative to `lor` could be
> `bitor; not; not` .
>
> Colin.
>
> On 08/11/2025 05:56, EricT wrote:
>> HI all,
>>
>> Well it isn't pretty, but now we have an equivalent test for land/lor,
>>
>> Took a few go-arounds with gemini to get the logic right, but here's
>> the replacements that work in both 8.6 and 9.x
>>
>> foreach {op prec code} {
>> ) 0 -
>> , 0 -
>> = 0 -
>> ? 1 -
>> : 1 -
>> || 2 {jumpTrue true_res; jumpFalse pop_A; push 1; jump end_res;
>> label pop_A; push 0; jump end_res; label true_res; pop; push 1; label
>> end_res}
>> && 3 {jumpFalse pop_A; jumpFalse false_res; push 1; jump end_res;
>> label pop_A; pop; label false_res; push 0; label end_res}
>> | 4 bitor
>> ^ 5 bitxor
>> & 6 bitand
>> ......
>>
>>
>> Not sure why they bothered to remove land/lor, couldn't have saved
>> much by removing it. Now that's I guess what the developers will not
>> like, that they can't just remove it if it goes public. But it was
>> after all an easy fix. The other codes are unlikely to go away any
>> time soon I would think. And a side by side compare shows that the
>> only other differences, at least according to the list output by
>> assemble if you give it an invalid one, are that these are all new to 9.x
>>
>> dictGetDef
>> dictPut
>> dictRemove
>> isEmpty
>> jumpTableNum
>> strge
>> strgt
>> strle
>> strlt
>> swap
>> tclooId
>>
>> Stack machines are kinda fun. And the assembler is actually pretty
>> friendly, it knew in advance that I was going to have a stack
>> imbalance, and then the next try gave a stack underrun, but it simply
>> threw an error with a nice message. Very cool I'd say. They ain't
>> kidding when they say, " Gemini can make mistakes, so double-check it
>> ". Anyone care to verify these, though my 146 + 8 more for the logic
>> test cases are all working on both 8.6 and 9.x.
>>
>> There is one thing, the code we're generating has to push all
>> variable names, unlike expr where the compiler is smarter and has
>> locals that it doesn't have to lookup by name each time. We can't use
>> a peephole optimiser, like the compiler, so expr will always have
>> that advantage. But we're in the same ballpark at least.
>>
>> Eric
>>
>>
>> On Fri, Nov 7, 2025 at 5:44 PM Phillip Brooks <phi...@um...> wrote:
>>
>> I would like to see something like this in the core. Then, there
>> is no concern about using the undocumented interface.
>>
>> Phil
>>
>> On Fri, Nov 7, 2025 at 5:40 PM Steve Landers
>> <st...@di...> wrote:
>>
>> I am very positive about Colin's approach, especially with
>> the speedups you (Eric) have recommended. In my opinion it
>> offers the best hope of improving the expr command without
>> breaking code or introducing Lots of Interminably Silly
>> Parentheses (see what I did there?).
>>
>> As for bytecode changes, as Phil notes they come with the
>> risk of making the solution release-dependent but I don't
>> think that should be seen as a show-stopper given the value
>> of an improved expr.
>>
>> Just my 2c worth
>>
>> -- Steve
>> On 8 Nov 2025 at 9:06 AM +0800, EricT <tw...@gm...>, wrote:
>>> Philip:
>>>
>>> Well, maybe it's a good thing that I don't have any tct
>>> votes. But now I have some pure tcl, that runs very fast and
>>> finally I can put the expr dragon to rest. I have several
>>> versions of tcl, and many of Ashok's great
>>> tclkit's bundled with twapi. I hope he finds a way to create
>>> more, especially for 9.x. I have some utilities that for
>>> some forgotten reason still need an older 8.6, and so that's
>>> how they launch.
>>>
>>> If you want to try my latest fork of Colin's masterpiece,
>>> you can just tclsh it and then take a look at the bytecode,
>>> as it dumps the cache at the end. There are now 140 test
>>> cases you can look through as examples of usage. The last
>>> 10 also use : as the command name, which I think of as a
>>> slimmer alias of = that just seems to look right to my eyes.
>>> But = works as well.
>>>
>>> Here's the very latest:
>>>
>>> https://github.com/rocketship88/colin-parser
>>>
>>> Eric
>>>
>>>
>>> On Fri, Nov 7, 2025 at 4:16 PM Phillip Brooks
>>> <phi...@um...> wrote:
>>>
>>>
>>>
>>> On Fri, Nov 7, 2025 at 2:14 PM EricT <tw...@gm...>
>>> wrote:
>>>
>>> Hi Phillip:
>>>
>>> In going from tcl 8.6 to tcl 9.0 the only things
>>> that broke, at least where Colin's program is
>>> concerned, were land and lor, which are no longer
>>> supported. If the bytecode were to have to support
>>> those two because they had been made public, I don't
>>> see the big harm. In 8.6 they'd already gone to a
>>> jump based arrangement for short circuiting. So, in
>>> 9.x they just removed these 2 instructions.
>>>
>>>
>>> The harm is that the bytecode is currently allowed to
>>> change completely from release to release. This allows
>>> for reorganization and optimization that would otherwise
>>> be impossible. The way you are using it is, I think,
>>> safe since you only ever create and run it in the same
>>> session. If opened up for general use, people might try
>>> to save it to a file, and then use that on a different
>>> release. Others are more expert in this area than I
>>> am, though.
>>>
>>> Phil
>>>
>>> _______________________________________________
>>> Tcl-Core mailing list
>>> Tcl...@li...
>>> https://lists.sourceforge.net/lists/listinfo/tcl-core
>>>
>>> _______________________________________________
>>> Tcl-Core mailing list
>>> Tcl...@li...
>>> https://lists.sourceforge.net/lists/listinfo/tcl-core
>>
>> _______________________________________________
>> Tcl-Core mailing list
>> Tcl...@li...
>> https://lists.sourceforge.net/lists/listinfo/tcl-core
>>
>>
>>
>> _______________________________________________
>> Tcl-Core mailing list
>> Tcl...@li...
>> https://lists.sourceforge.net/lists/listinfo/tcl-core
>
>
> _______________________________________________
> Tcl-Core mailing list
> Tcl...@li...
> https://lists.sourceforge.net/lists/listinfo/tcl-core |
|
From: EricT <tw...@gm...> - 2025-11-08 22:50:47
|
Did you try this on 8.6 or 9.x, if the error message is to be believed,
then swap is new to 9.x.
Eric
On Sat, Nov 8, 2025 at 7:03 AM Colin Macleod via Tcl-Core <
tcl...@li...> wrote:
> Similarly, if we restrict to numeric operands, an alternative to `land`
> would be `not; swap; not; bitor; not` . However when I try this
> ::tcl::unsupported::assemble tells me `swap` is not a valid instruction,
> although it is listed in generic/tclAssembly.c 😮
>
> Colin.
> On 08/11/2025 08:45, Colin Macleod via Tcl-Core wrote:
>
> I haven't tested this, but a simpler alternative to `lor` could be `bitor;
> not; not` .
>
> Colin.
> On 08/11/2025 05:56, EricT wrote:
>
> HI all,
>
> Well it isn't pretty, but now we have an equivalent test for land/lor,
>
> Took a few go-arounds with gemini to get the logic right, but here's the
> replacements that work in both 8.6 and 9.x
>
> foreach {op prec code} {
> ) 0 -
> , 0 -
> = 0 -
> ? 1 -
> : 1 -
> || 2 {jumpTrue true_res; jumpFalse pop_A; push 1; jump end_res; label
> pop_A; push 0; jump end_res; label true_res; pop; push 1; label end_res}
> && 3 {jumpFalse pop_A; jumpFalse false_res; push 1; jump end_res;
> label pop_A; pop; label false_res; push 0; label end_res}
> | 4 bitor
> ^ 5 bitxor
> & 6 bitand
> ......
>
>
> Not sure why they bothered to remove land/lor, couldn't have saved much by
> removing it. Now that's I guess what the developers will not like, that
> they can't just remove it if it goes public. But it was after all an easy
> fix. The other codes are unlikely to go away any time soon I would think.
> And a side by side compare shows that the only other differences, at least
> according to the list output by assemble if you give it an invalid one, are
> that these are all new to 9.x
>
> dictGetDef
> dictPut
> dictRemove
> isEmpty
> jumpTableNum
> strge
> strgt
> strle
> strlt
> swap
> tclooId
>
> Stack machines are kinda fun. And the assembler is actually pretty
> friendly, it knew in advance that I was going to have a stack imbalance,
> and then the next try gave a stack underrun, but it simply threw an error
> with a nice message. Very cool I'd say. They ain't kidding when they say, "
> Gemini can make mistakes, so double-check it ". Anyone care to verify
> these, though my 146 + 8 more for the logic test cases are all working on
> both 8.6 and 9.x.
>
> There is one thing, the code we're generating has to push all
> variable names, unlike expr where the compiler is smarter and has locals
> that it doesn't have to lookup by name each time. We can't use a peephole
> optimiser, like the compiler, so expr will always have that advantage. But
> we're in the same ballpark at least.
>
> Eric
>
>
> On Fri, Nov 7, 2025 at 5:44 PM Phillip Brooks <phi...@um...> wrote:
>
>> I would like to see something like this in the core. Then, there is no
>> concern about using the undocumented interface.
>>
>> Phil
>>
>> On Fri, Nov 7, 2025 at 5:40 PM Steve Landers <st...@di...>
>> wrote:
>>
>>> I am very positive about Colin's approach, especially with the speedups
>>> you (Eric) have recommended. In my opinion it offers the best hope of
>>> improving the expr command without breaking code or introducing Lots of
>>> Interminably Silly Parentheses (see what I did there?).
>>>
>>> As for bytecode changes, as Phil notes they come with the risk of
>>> making the solution release-dependent but I don't think that should be seen
>>> as a show-stopper given the value of an improved expr.
>>>
>>> Just my 2c worth
>>>
>>> -- Steve
>>> On 8 Nov 2025 at 9:06 AM +0800, EricT <tw...@gm...>, wrote:
>>>
>>> Philip:
>>>
>>> Well, maybe it's a good thing that I don't have any tct votes. But now I
>>> have some pure tcl, that runs very fast and finally I can put the expr
>>> dragon to rest. I have several versions of tcl, and many of Ashok's great
>>> tclkit's bundled with twapi. I hope he finds a way to create more,
>>> especially for 9.x. I have some utilities that for some forgotten reason
>>> still need an older 8.6, and so that's how they launch.
>>>
>>> If you want to try my latest fork of Colin's masterpiece, you can just
>>> tclsh it and then take a look at the bytecode, as it dumps the cache at the
>>> end. There are now 140 test cases you can look through as examples of
>>> usage. The last 10 also use : as the command name, which I think of as a
>>> slimmer alias of = that just seems to look right to my eyes. But = works as
>>> well.
>>>
>>> Here's the very latest:
>>>
>>> https://github.com/rocketship88/colin-parser
>>>
>>> Eric
>>>
>>>
>>> On Fri, Nov 7, 2025 at 4:16 PM Phillip Brooks <phi...@um...>
>>> wrote:
>>>
>>>>
>>>>
>>>> On Fri, Nov 7, 2025 at 2:14 PM EricT <tw...@gm...> wrote:
>>>>
>>>>> Hi Phillip:
>>>>>
>>>>> In going from tcl 8.6 to tcl 9.0 the only things that broke, at least
>>>>> where Colin's program is concerned, were land and lor, which are no longer
>>>>> supported. If the bytecode were to have to support those two because they
>>>>> had been made public, I don't see the big harm. In 8.6 they'd already gone
>>>>> to a jump based arrangement for short circuiting. So, in 9.x they just
>>>>> removed these 2 instructions.
>>>>>
>>>>
>>>> The harm is that the bytecode is currently allowed to change completely
>>>> from release to release. This allows for reorganization and optimization
>>>> that would otherwise be impossible. The way you are using it is, I think,
>>>> safe since you only ever create and run it in the same session. If opened
>>>> up for general use, people might try to save it to a file, and then use
>>>> that on a different release. Others are more expert in this area than I
>>>> am, though.
>>>>
>>>> Phil
>>>>
>>>>> _______________________________________________
>>>> Tcl-Core mailing list
>>>> Tcl...@li...
>>>> https://lists.sourceforge.net/lists/listinfo/tcl-core
>>>>
>>> _______________________________________________
>>> Tcl-Core mailing list
>>> Tcl...@li...
>>> https://lists.sourceforge.net/lists/listinfo/tcl-core
>>>
>>> _______________________________________________
>> Tcl-Core mailing list
>> Tcl...@li...
>> https://lists.sourceforge.net/lists/listinfo/tcl-core
>>
>
>
> _______________________________________________
> Tcl-Core mailing lis...@li...://lists.sourceforge.net/lists/listinfo/tcl-core
>
>
>
> _______________________________________________
> Tcl-Core mailing lis...@li...://lists.sourceforge.net/lists/listinfo/tcl-core
>
> _______________________________________________
> Tcl-Core mailing list
> Tcl...@li...
> https://lists.sourceforge.net/lists/listinfo/tcl-core
>
|
|
From: Donald P. <d.g...@co...> - 2025-11-08 23:02:38
|
One of the reasons [assemble] is unsupported is because no one is tasked with keeping it up to date with new bytecodes.
There is considerable value in not exposing and committing to every internal detail of your software.
DGP
> On Nov 8, 2025, at 5:50 PM, EricT <tw...@gm...> wrote:
>
> Did you try this on 8.6 or 9.x, if the error message is to be believed, then swap is new to 9.x.
>
> Eric
>
> On Sat, Nov 8, 2025 at 7:03 AM Colin Macleod via Tcl-Core <tcl...@li... <mailto:tcl...@li...>> wrote:
>> Similarly, if we restrict to numeric operands, an alternative to `land` would be `not; swap; not; bitor; not` . However when I try this ::tcl::unsupported::assemble tells me `swap` is not a valid instruction, although it is listed in generic/tclAssembly.c 😮
>>
>> Colin.
>>
>> On 08/11/2025 08:45, Colin Macleod via Tcl-Core wrote:
>>> I haven't tested this, but a simpler alternative to `lor` could be `bitor; not; not` .
>>>
>>> Colin.
>>>
>>> On 08/11/2025 05:56, EricT wrote:
>>>> HI all,
>>>>
>>>> Well it isn't pretty, but now we have an equivalent test for land/lor,
>>>>
>>>> Took a few go-arounds with gemini to get the logic right, but here's the replacements that work in both 8.6 and 9.x
>>>>
>>>> foreach {op prec code} {
>>>> ) 0 -
>>>> , 0 -
>>>> = 0 -
>>>> ? 1 -
>>>> : 1 -
>>>> || 2 {jumpTrue true_res; jumpFalse pop_A; push 1; jump end_res; label pop_A; push 0; jump end_res; label true_res; pop; push 1; label end_res}
>>>> && 3 {jumpFalse pop_A; jumpFalse false_res; push 1; jump end_res; label pop_A; pop; label false_res; push 0; label end_res}
>>>> | 4 bitor
>>>> ^ 5 bitxor
>>>> & 6 bitand
>>>> ......
>>>>
>>>>
>>>> Not sure why they bothered to remove land/lor, couldn't have saved much by removing it. Now that's I guess what the developers will not like, that they can't just remove it if it goes public. But it was after all an easy fix. The other codes are unlikely to go away any time soon I would think. And a side by side compare shows that the only other differences, at least according to the list output by assemble if you give it an invalid one, are that these are all new to 9.x
>>>>
>>>> dictGetDef
>>>> dictPut
>>>> dictRemove
>>>> isEmpty
>>>> jumpTableNum
>>>> strge
>>>> strgt
>>>> strle
>>>> strlt
>>>> swap
>>>> tclooId
>>>>
>>>> Stack machines are kinda fun. And the assembler is actually pretty friendly, it knew in advance that I was going to have a stack imbalance, and then the next try gave a stack underrun, but it simply threw an error with a nice message. Very cool I'd say. They ain't kidding when they say, " Gemini can make mistakes, so double-check it ". Anyone care to verify these, though my 146 + 8 more for the logic test cases are all working on both 8.6 and 9.x.
>>>>
>>>> There is one thing, the code we're generating has to push all variable names, unlike expr where the compiler is smarter and has locals that it doesn't have to lookup by name each time. We can't use a peephole optimiser, like the compiler, so expr will always have that advantage. But we're in the same ballpark at least.
>>>>
>>>> Eric
>>>>
>>>>
>>>> On Fri, Nov 7, 2025 at 5:44 PM Phillip Brooks <phi...@um... <mailto:phi...@um...>> wrote:
>>>>> I would like to see something like this in the core. Then, there is no concern about using the undocumented interface.
>>>>>
>>>>> Phil
>>>>>
>>>>> On Fri, Nov 7, 2025 at 5:40 PM Steve Landers <st...@di... <mailto:st...@di...>> wrote:
>>>>>> I am very positive about Colin's approach, especially with the speedups you (Eric) have recommended. In my opinion it offers the best hope of improving the expr command without breaking code or introducing Lots of Interminably Silly Parentheses (see what I did there?).
>>>>>>
>>>>>> As for bytecode changes, as Phil notes they come with the risk of making the solution release-dependent but I don't think that should be seen as a show-stopper given the value of an improved expr.
>>>>>>
>>>>>> Just my 2c worth
>>>>>>
>>>>>> -- Steve
>>>>>> On 8 Nov 2025 at 9:06 AM +0800, EricT <tw...@gm... <mailto:tw...@gm...>>, wrote:
>>>>>>> Philip:
>>>>>>>
>>>>>>> Well, maybe it's a good thing that I don't have any tct votes. But now I have some pure tcl, that runs very fast and finally I can put the expr dragon to rest. I have several versions of tcl, and many of Ashok's great tclkit's bundled with twapi. I hope he finds a way to create more, especially for 9.x. I have some utilities that for some forgotten reason still need an older 8.6, and so that's how they launch.
>>>>>>>
>>>>>>> If you want to try my latest fork of Colin's masterpiece, you can just tclsh it and then take a look at the bytecode, as it dumps the cache at the end. There are now 140 test cases you can look through as examples of usage. The last 10 also use : as the command name, which I think of as a slimmer alias of = that just seems to look right to my eyes. But = works as well.
>>>>>>>
>>>>>>> Here's the very latest:
>>>>>>>
>>>>>>> https://github.com/rocketship88/colin-parser
>>>>>>>
>>>>>>> Eric
>>>>>>>
>>>>>>>
>>>>>>> On Fri, Nov 7, 2025 at 4:16 PM Phillip Brooks <phi...@um... <mailto:phi...@um...>> wrote:
>>>>>>>>
>>>>>>>>
>>>>>>>> On Fri, Nov 7, 2025 at 2:14 PM EricT <tw...@gm... <mailto:tw...@gm...>> wrote:
>>>>>>>>> Hi Phillip:
>>>>>>>>>
>>>>>>>>> In going from tcl 8.6 to tcl 9.0 the only things that broke, at least where Colin's program is concerned, were land and lor, which are no longer supported. If the bytecode were to have to support those two because they had been made public, I don't see the big harm. In 8.6 they'd already gone to a jump based arrangement for short circuiting. So, in 9.x they just removed these 2 instructions.
>>>>>>>>
>>>>>>>> The harm is that the bytecode is currently allowed to change completely from release to release. This allows for reorganization and optimization that would otherwise be impossible. The way you are using it is, I think, safe since you only ever create and run it in the same session. If opened up for general use, people might try to save it to a file, and then use that on a different release. Others are more expert in this area than I am, though.
>>>>>>>>
>>>>>>>> Phil
>>>>>>>> _______________________________________________
>>>>>>>> Tcl-Core mailing list
>>>>>>>> Tcl...@li... <mailto:Tcl...@li...>
>>>>>>>> https://lists.sourceforge.net/lists/listinfo/tcl-core
>>>>>>> _______________________________________________
>>>>>>> Tcl-Core mailing list
>>>>>>> Tcl...@li... <mailto:Tcl...@li...>
>>>>>>> https://lists.sourceforge.net/lists/listinfo/tcl-core
>>>>> _______________________________________________
>>>>> Tcl-Core mailing list
>>>>> Tcl...@li... <mailto:Tcl...@li...>
>>>>> https://lists.sourceforge.net/lists/listinfo/tcl-core
>>>>
>>>>
>>>>
>>>> _______________________________________________
>>>> Tcl-Core mailing list
>>>> Tcl...@li... <mailto:Tcl...@li...>
>>>> https://lists.sourceforge.net/lists/listinfo/tcl-core
>>>
>>>
>>>
>>> _______________________________________________
>>> Tcl-Core mailing list
>>> Tcl...@li... <mailto:Tcl...@li...>
>>> https://lists.sourceforge.net/lists/listinfo/tcl-core
>> _______________________________________________
>> Tcl-Core mailing list
>> Tcl...@li... <mailto:Tcl...@li...>
>> https://lists.sourceforge.net/lists/listinfo/tcl-core
> _______________________________________________
> Tcl-Core mailing list
> Tcl...@li...
> https://lists.sourceforge.net/lists/listinfo/tcl-core
|
|
From: EricT <tw...@gm...> - 2025-11-09 07:04:29
|
Hi Don:
I understand your concern. But if history is any guide, it's far easier to
get tcl to stay the same than to change. I doubt there will be any
incompatibilities with the use of the assemble command, at least not in my
lifetime. And if I were to live to see it, I expect I'll just ask my robot
to fix the code. The one I use now is a very good C and Tcl programmer. And
as Perry White said to Lois Lane when he hired Clark Kent: In my 40 years
in this business, he's the fastest typist I've ever seen.
So, I've put the final touches on the Colin fork, putting everything except
one global into a Calc:: namespace. It works just fine now in 8.6 and 9.x.
and so I'll leave this debate to others. I want to thank everyone for their
comments. It has been quite an interesting month.
Eric
On Sat, Nov 8, 2025 at 3:02 PM Donald Porter via Tcl-Core <
tcl...@li...> wrote:
> One of the reasons [assemble] is unsupported is because no one is tasked
> with keeping it up to date with new bytecodes.
>
> There is considerable value in not exposing and committing to every
> internal detail of your software.
>
> DGP
>
>
> On Nov 8, 2025, at 5:50 PM, EricT <tw...@gm...> wrote:
>
> Did you try this on 8.6 or 9.x, if the error message is to be believed,
> then swap is new to 9.x.
>
> Eric
>
> On Sat, Nov 8, 2025 at 7:03 AM Colin Macleod via Tcl-Core <
> tcl...@li...> wrote:
>
>> Similarly, if we restrict to numeric operands, an alternative to `land`
>> would be `not; swap; not; bitor; not` . However when I try this
>> ::tcl::unsupported::assemble tells me `swap` is not a valid instruction,
>> although it is listed in generic/tclAssembly.c 😮
>>
>> Colin.
>> On 08/11/2025 08:45, Colin Macleod via Tcl-Core wrote:
>>
>> I haven't tested this, but a simpler alternative to `lor` could be
>> `bitor; not; not` .
>>
>> Colin.
>> On 08/11/2025 05:56, EricT wrote:
>>
>> HI all,
>>
>> Well it isn't pretty, but now we have an equivalent test for land/lor,
>>
>> Took a few go-arounds with gemini to get the logic right, but here's the
>> replacements that work in both 8.6 and 9.x
>>
>> foreach {op prec code} {
>> ) 0 -
>> , 0 -
>> = 0 -
>> ? 1 -
>> : 1 -
>> || 2 {jumpTrue true_res; jumpFalse pop_A; push 1; jump end_res; label
>> pop_A; push 0; jump end_res; label true_res; pop; push 1; label end_res}
>> && 3 {jumpFalse pop_A; jumpFalse false_res; push 1; jump end_res;
>> label pop_A; pop; label false_res; push 0; label end_res}
>> | 4 bitor
>> ^ 5 bitxor
>> & 6 bitand
>> ......
>>
>>
>> Not sure why they bothered to remove land/lor, couldn't have saved much
>> by removing it. Now that's I guess what the developers will not like, that
>> they can't just remove it if it goes public. But it was after all an easy
>> fix. The other codes are unlikely to go away any time soon I would think.
>> And a side by side compare shows that the only other differences, at least
>> according to the list output by assemble if you give it an invalid one, are
>> that these are all new to 9.x
>>
>> dictGetDef
>> dictPut
>> dictRemove
>> isEmpty
>> jumpTableNum
>> strge
>> strgt
>> strle
>> strlt
>> swap
>> tclooId
>>
>> Stack machines are kinda fun. And the assembler is actually pretty
>> friendly, it knew in advance that I was going to have a stack imbalance,
>> and then the next try gave a stack underrun, but it simply threw an error
>> with a nice message. Very cool I'd say. They ain't kidding when they say, "
>> Gemini can make mistakes, so double-check it ". Anyone care to verify
>> these, though my 146 + 8 more for the logic test cases are all working on
>> both 8.6 and 9.x.
>>
>> There is one thing, the code we're generating has to push all
>> variable names, unlike expr where the compiler is smarter and has locals
>> that it doesn't have to lookup by name each time. We can't use a peephole
>> optimiser, like the compiler, so expr will always have that advantage. But
>> we're in the same ballpark at least.
>>
>> Eric
>>
>>
>> On Fri, Nov 7, 2025 at 5:44 PM Phillip Brooks <phi...@um...> wrote:
>>
>>> I would like to see something like this in the core. Then, there is no
>>> concern about using the undocumented interface.
>>>
>>> Phil
>>>
>>> On Fri, Nov 7, 2025 at 5:40 PM Steve Landers <st...@di...>
>>> wrote:
>>>
>>>> I am very positive about Colin's approach, especially with the speedups
>>>> you (Eric) have recommended. In my opinion it offers the best hope of
>>>> improving the expr command without breaking code or introducing Lots of
>>>> Interminably Silly Parentheses (see what I did there?).
>>>>
>>>> As for bytecode changes, as Phil notes they come with the risk of
>>>> making the solution release-dependent but I don't think that should be seen
>>>> as a show-stopper given the value of an improved expr.
>>>>
>>>> Just my 2c worth
>>>>
>>>> -- Steve
>>>> On 8 Nov 2025 at 9:06 AM +0800, EricT <tw...@gm...>, wrote:
>>>>
>>>> Philip:
>>>>
>>>> Well, maybe it's a good thing that I don't have any tct votes. But now
>>>> I have some pure tcl, that runs very fast and finally I can put the expr
>>>> dragon to rest. I have several versions of tcl, and many of Ashok's great
>>>> tclkit's bundled with twapi. I hope he finds a way to create more,
>>>> especially for 9.x. I have some utilities that for some forgotten reason
>>>> still need an older 8.6, and so that's how they launch.
>>>>
>>>> If you want to try my latest fork of Colin's masterpiece, you can just
>>>> tclsh it and then take a look at the bytecode, as it dumps the cache at the
>>>> end. There are now 140 test cases you can look through as examples of
>>>> usage. The last 10 also use : as the command name, which I think of as a
>>>> slimmer alias of = that just seems to look right to my eyes. But = works as
>>>> well.
>>>>
>>>> Here's the very latest:
>>>>
>>>> https://github.com/rocketship88/colin-parser
>>>>
>>>> Eric
>>>>
>>>>
>>>> On Fri, Nov 7, 2025 at 4:16 PM Phillip Brooks <phi...@um...>
>>>> wrote:
>>>>
>>>>>
>>>>>
>>>>> On Fri, Nov 7, 2025 at 2:14 PM EricT <tw...@gm...> wrote:
>>>>>
>>>>>> Hi Phillip:
>>>>>>
>>>>>> In going from tcl 8.6 to tcl 9.0 the only things that broke, at least
>>>>>> where Colin's program is concerned, were land and lor, which are no longer
>>>>>> supported. If the bytecode were to have to support those two because they
>>>>>> had been made public, I don't see the big harm. In 8.6 they'd already gone
>>>>>> to a jump based arrangement for short circuiting. So, in 9.x they just
>>>>>> removed these 2 instructions.
>>>>>>
>>>>>
>>>>> The harm is that the bytecode is currently allowed to change
>>>>> completely from release to release. This allows for reorganization and
>>>>> optimization that would otherwise be impossible. The way you are using it
>>>>> is, I think, safe since you only ever create and run it in the same
>>>>> session. If opened up for general use, people might try to save it to a
>>>>> file, and then use that on a different release. Others are more expert in
>>>>> this area than I am, though.
>>>>>
>>>>> Phil
>>>>>
>>>>>> _______________________________________________
>>>>> Tcl-Core mailing list
>>>>> Tcl...@li...
>>>>> https://lists.sourceforge.net/lists/listinfo/tcl-core
>>>>>
>>>> _______________________________________________
>>>> Tcl-Core mailing list
>>>> Tcl...@li...
>>>> https://lists.sourceforge.net/lists/listinfo/tcl-core
>>>>
>>>> _______________________________________________
>>> Tcl-Core mailing list
>>> Tcl...@li...
>>> https://lists.sourceforge.net/lists/listinfo/tcl-core
>>>
>>
>>
>> _______________________________________________
>> Tcl-Core mailing lis...@li...://lists.sourceforge.net/lists/listinfo/tcl-core
>>
>>
>>
>> _______________________________________________
>> Tcl-Core mailing lis...@li...://lists.sourceforge.net/lists/listinfo/tcl-core
>>
>> _______________________________________________
>> Tcl-Core mailing list
>> Tcl...@li...
>> https://lists.sourceforge.net/lists/listinfo/tcl-core
>>
> _______________________________________________
> Tcl-Core mailing list
> Tcl...@li...
> https://lists.sourceforge.net/lists/listinfo/tcl-core
>
>
> _______________________________________________
> Tcl-Core mailing list
> Tcl...@li...
> https://lists.sourceforge.net/lists/listinfo/tcl-core
>
|
|
From: Zaumseil R. <RZa...@kk...> - 2025-11-10 07:21:03
|
Hi Eric
Thank you for your effort to bring this topic forward.
Imho Colin's syntax is even better as the let version.
I hope one of your proposals will make it into the core.
Regards
rene
Von: EricT <tw...@gm...>
Gesendet: Freitag, 7. November 2025 21:35
An: Zaumseil René <RZa...@kk...>
Cc: tc...@ro...; tcl...@li...
Betreff: Re: [TCLCORE] [Ext] Re: [=] for concise expressions (was Re: TIP 672 Implementation Complete - Ready for Sponsorship)
Rene,
That's the beauty of making bytecode compilation official - we DON'T have to agree on syntax! With a supported tcl::bytecode API, each of us can create our own Design-Specific Language for our own needs. No more "one size fits all" debates that go nowhere.
That said, I think you might find Colin's approach already fits your needs quite well. Here's how your examples would look:
Multiple assignments:
# Your let syntax
let {x 1+2 y 3*4 z $x+$y}
# Colin's syntax (bare variables, no $)
: {x = 1+2
y = 3*4
z = x+y}
For my own taste, I've here aliased = to : since = as the command seems to clash visually with = for assignment, especially in cases like a = b = c = d. The semicolon requires braces since it's special to Tcl, but by mapping newlines to semicolons internally, multiple statements work naturally in braced blocks as shown above.
Returning multiple values:
# With a simple helper function
proc tcl::mathfunc::gather {args} { list {*}$args }
# Your canvas example
.c create text {*}[= gather(x+1, y+1)] -text a
# Or inline
= {tx = x+1 ; ty = y+1 ; gather(tx, ty)}
Your calculations example:
set i 0.5
= {x = sin(i)
y = cos(i) + x
z = x + y}
The key differences: bare variables (no $), explicit = for assignment, semicolon (or newline) to separate multiple expressions, and gather() for multiple returns. But it's all extensible - add your own functions to tcl::mathfunc:: and they're automatically available.
If you prefer different syntax, you can build your let command using the same bytecode infrastructure. That's the whole point - multiple DSLs coexisting, each optimized by bytecode caching.
Eric
On Fri, Nov 7, 2025 at 6:19 AM Zaumseil René via Tcl-Core <tcl...@li...<mailto:tcl...@li...>> wrote:
Hi Eric
I really like the tcl::unsupported::assemble approach.
It opens the way to add more commands/sugar to tcl itself without
having to hard code it in C.
Now we have to agree on the syntax. May be a wiki page with a summary of the different proposals?
Regards
rene
Von: EricT <tw...@gm...<mailto:tw...@gm...>>
Gesendet: Freitag, 7. November 2025 01:03
An: Zaumseil René <RZa...@kk...<mailto:RZa...@kk...>>; tcl...@li...<mailto:tcl...@li...>
Betreff: Re: [TCLCORE] [Ext] Re: [=] for concise expressions (was Re: TIP 672 Implementation Complete - Ready for Sponsorship)
Hi Rene,
I was experimenting with your TIP 674 prototype and realized both your let command and Colin's expression evaluator share something important: they could both be implemented in pure Tcl if tcl::unsupported::assemble were promoted to supported status with handle-based caching.
This would avoid another round of "expr wars" where lack of consensus leaves us with nothing. Instead of debating whose syntax is better, we'd each have the power to create our own Design-Specific Language. With bytecode caching, pure Tcl implementations achieve C extension performance - Colin's evaluator runs at 1.8 microseconds, nearly as fast as expr.
Why does this matter for your TIP? If let performs just as well in Tcl as in C, why require a C implementation at all? C extensions take weeks to develop, need binaries for every platform/architecture combination, and ultimately call the same bytecode infrastructure that assemble uses. A supported bytecode API would make both proposals - and many others - viable without C.
Perhaps we should advocate together for making the bytecode compilation infrastructure officially supported, rather than competing for TIP approval on individual commands?
Best,
Eric
On Wed, Nov 5, 2025 at 11:31 PM Zaumseil René via Tcl-Core <tcl...@li...<mailto:tcl...@li...>> wrote:
Hello
I also like the = approach as a new expr command without need of $ for variables.
But I'm not sure about the currently proposed syntax.
Is it "= 1 + 2" or "= 1+2" (with or without spaces)?
If it is the later then I would propose to use some of the syntax of the "let" from tip 674.
Some syntax ideas:
"= 1+2" return 3, simple case
"= a 1+2 b 2+4 c a+b" return {3 6 9} and set a,b,c
"= 1+2 = 2+4" return {3 6}
And may be some others…
Regards
rene
Von: EricT <tw...@gm...<mailto:tw...@gm...>>
Gesendet: Mittwoch, 5. November 2025 23:22
An: Colin Macleod <col...@ya...<mailto:col...@ya...>>; tcl...@li...<mailto:tcl...@li...>
Betreff: [Ext] Re: [TCLCORE] [=] for concise expressions (was Re: TIP 672 Implementation Complete - Ready for Sponsorship)
Hi Colin,
I've successfully modified your amazing code to handle arrays. In doing so, I also found 2 other issues, one is with your Boolean check, the other with your function name check, both because of [string is] issues.
- Boolean check: `$token eq "false" || $token eq "true"` (was `[string is boolean $token]` - treated 'f','n', 't', 'y', etc. variables as boolean false, no, true, yes, ...)
- Function check: `[regexp {^[[:alpha:]]} $token]` (was `[string is alpha $token]` - broke log10, atan2)
here's the code for arrays:
# Function call or array reference?
set nexttok [lindex $::tokens $::tokpos]
if {$nexttok eq "(" && [regexp {^[[:alpha:]]} $token]} {
set fun [namespace which tcl::mathfunc::$token]
if {$fun ne {}} {
# It's a function
incr ::tokpos
set opcodes "push $fun; "
append opcodes [parseFuncArgs]
return $opcodes
} else {
# Not a function, assume array reference
incr ::tokpos
set opcodes "push $token; "
# Parse the index expression - leaves VALUE on stack
append opcodes [parse 0]
# Expect closing paren
set closing [lindex $::tokens $::tokpos]
if {$closing ne ")"} {
error "Calc: expected ')' but found '$closing'"
}
# Stack now has: [arrayname, indexvalue]
incr ::tokpos
append opcodes "loadArrayStk; "
return $opcodes
}
}
In addition, there has indeed been some changes in the bytecode, land and lor are no longer supported in 9.0 although they work in 8.6.
I had an AI generate some 117 test cases, which all pass on 8.6 and 111 on 9.x (the land/lor not being tested in 9.x).
Colin, with your permission, I can post the code as a new file, with all the test cases, say on a repository at github.
I think a new TIP is worth considering; one that promotes assemble to a supported form, with a compile and handle approach to avoid the time parsing the ascii byte code text. I think that this would be great for your = command, but also quite useful for others who might want to create their own little languages.
By doing it this way, it remains pure tcl, and avoids all the problems with different systems and hardware that a binary extension would create. In the end, I believe your code can achieve performance parity with expr. Not only does it remove half the [expr {...}] baggage, but all the $'s too! So much easier on these old eyes.
Regards,
Eric
On Tue, Nov 4, 2025 at 1:06 PM EricT <tw...@gm...<mailto:tw...@gm...>> wrote:
Hi Colin,
Hmmm, why can't you do bareword on $a(b) as a(b) you just need to do an uplevel to see if a is a variable, if not, it would have to be a function. True?
% tcl::unsupported::disassemble script {set a [expr {$b($c)}] }
snip
Command 2: "expr {$b($c)}..."
(2) push1 1 # "b"
(4) push1 2 # "c"
(6) loadStk
(7) loadArrayStk
(8) tryCvtToNumeric
(9) storeStk
(10) done
This doesn't look too much different from what you are producing.
I think what's really needed here is a TIP that would open up the bytecode a bit so you don't need to use an unsupported command. And then maybe even have a new command to take the string byte code you are now producing and return a handle to a cached version that was probably equivalent to the existing bytecode. Then your cache array would be
set cache($exp) $handle
Instead of it having to parse the text, it could be as fast as bytecode. You'd likely be just as fast as expr, and safe as well, since you can't pass a string command in where the bareword is required:
% set x {[pwd]}
[pwd]
% = sqrt(x)
exp= |sqrt(x)| code= |push ::tcl::mathfunc::sqrt; push x; loadStk; invokeStk 2; | ifexist: 0
expected floating-point number but got "[pwd]"
I think you really have something here, perhaps this is the best answer yet to slay the expr dragon!
Regards,
Eric
On Tue, Nov 4, 2025 at 6:52 AM Colin Macleod via Tcl-Core <tcl...@li...<mailto:tcl...@li...>> wrote:
Hi Eric,
That's very neat!
Yes, a pure Tcl version could go into TclLib. I still think it may be worth trying a C implementation though. The work-around that's needed for array references [= 2* $a(b)] would defeat the caching, so it would be good to speed up the parsing if possible. Also I think your caching may be equivalent to doing byte-compilation, in which case it may make sense to use the framework which already exists for that.
Colin.
On 04/11/2025 01:18, EricT wrote:
that is:
if {[info exist ::cache($exp)]} {
tailcall ::tcl::unsupported::assemble $::cache($exp)
}
(hate gmail!)
On Mon, Nov 3, 2025 at 5:17 PM EricT <tw...@gm...<mailto:tw...@gm...>> wrote:
and silly of me, it should be:
if {[info exist ::cache($exp)]} {
tailcall ::tcl::unsupported::assemble $::cache($exp)
}
On Mon, Nov 3, 2025 at 4:50 PM EricT <tw...@gm...<mailto:tw...@gm...>> wrote:
With a debug line back in plus the tailcall:
proc = args {
set exp [join $args]
if { [info exist ::cache($exp)] } {
return [tailcall ::tcl::unsupported::assemble $::cache($exp)]
}
set tokens [tokenise $exp]
deb1 "TOKENS = '$tokens'"
set code [compile $tokens]
deb1 "GENERATED CODE:\n$code\n"
puts "exp= |$exp| code= |$code| ifexist: [info exist ::cache($exp)]"
set ::cache($exp) $code
uplevel [list ::tcl::unsupported::assemble $code]
}
% set a 5
5
% set b 10
10
% = a + b
exp= |a + b| code= |push a; loadStk; push b; loadStk; add; | ifexist: 0
15
% = a + b
15
% time {= a + b} 1000
1.73 microseconds per iteration
Faster still!
I thought the uplevel was needed to be able to get the local variables, seems not.
% proc foo arg {set a 5; set b 10; set c [= a+b+arg]}
% foo 5
exp= |a+b+arg| code= |push a; loadStk; push b; loadStk; add; push arg; loadStk; add; | ifexist: 0
20
% foo 5
20
% proc foo arg {global xxx; set a 5; set b 10; set c [= a+b+arg+xxx]}
% set xxx 100
100
% foo 200
315
% time {foo 200} 10000
2.1775 microseconds per iteration
% parray cache
cache(a + b) = push a; loadStk; push b; loadStk; add;
cache(a+b+arg) = push a; loadStk; push b; loadStk; add; push arg; loadStk; add;
cache(a+b+arg+xxx) = push a; loadStk; push b; loadStk; add; push arg; loadStk; add; push xxx; loadStk; add;
Very Impressive, great job Colin! Great catch Don!
Eric
On Mon, Nov 3, 2025 at 4:22 PM Donald Porter via Tcl-Core <tcl...@li...<mailto:tcl...@li...>> wrote:
Check what effect replacing [uplevel] with [tailcall] has.
On Nov 3, 2025, at 7:13 PM, EricT <tw...@gm...<mailto:tw...@gm...>> wrote:
Subject: Your bytecode expression evaluator - impressive results with caching!
Hey Colin:
I took a look at your bytecode-based expression evaluator and was intrigued by the approach. I made a small modification to add caching and the results are really impressive. Here's what I changed:
proc = args {
set exp [join $args]
if {[info exist ::cache($exp)]} {
return [uplevel [list ::tcl::unsupported::assemble $::cache($exp)]]
}
set tokens [tokenise $exp]
deb1 "TOKENS = '$tokens'"
set code [compile $tokens]
deb1 "GENERATED CODE:\n$code\n"
set ::cache($exp) $code
uplevel [list ::tcl::unsupported::assemble $code]
}
The cache is just a simple array lookup - one line to store, one line to retrieve. But the performance impact is huge:
Performance Tests
Without caching
% time {= 1 + 2} 1000
24.937 microseconds per iteration
With caching
% time {= 1 + 2} 1000
1.8 microseconds per iteration
That's a 13x speedup! The tokenize and parse steps were eating about 92% of the execution time.
The Real Magic: Bare Variables + Caching
What really impressed me is how well your bare variable feature synergizes with caching:
% set a 5
5
% set b 6
6
% = a + b
11
% time {= a + b} 1000
2.079 microseconds per iteration
Now change the variable values
% set a 10
10
% = a + b
16
% time {= a + b} 1000
2.188 microseconds per iteration
The cache entry stays valid even when the variable values change! Why? Because the bytecode stores variable names, not values:
push a; loadStk; push b; loadStk; add;
The loadStk instruction does runtime lookup, so:
- Cache key is stable: "a + b"
- Works for any values of a and b
- One cache entry handles all value combinations
Compare this to if we used $-substitution:
= $a + $b # With a=5, b=6 becomes "5 + 6"
= $a + $b # With a=10, b=6 becomes "10 + 6" - different cache key!
Every value change would create a new cache entry or worse, a cache miss.
Comparison to Other Approaches
Tcl's expr: about 0.40 microseconds
Direct C evaluator: about 0.53 microseconds
Your cached approach: about 1.80 microseconds
Your uncached approach: about 24.9 microseconds
With caching, you're only 3-4x slower than a direct C evaluator.
My Assessment
Your design is excellent. The bare variable feature isn't just syntax sugar - it's essential for good cache performance. The synergy between:
1. Bare variables leading to stable cache keys
2. Runtime lookup keeping cache hot
3. Simple caching providing dramatic speedup
makes this really elegant.
My recommendation: Keep it in Tcl! The implementation is clean, performance is excellent (1.8 microseconds is plenty fast), and converting to C would add significant complexity for minimal gain (maybe getting to about 1.0 microseconds).
The Tcl prototype with caching is actually the right solution here. Sometimes the prototype IS the product!
Excellent work on this. The bytecode approach really shines with caching enabled.
On Sun, Nov 2, 2025 at 10:14 AM Colin Macleod via Tcl-Core <tcl...@li...<mailto:tcl...@li...>> wrote:
Hi again,
I've now made a slightly more serious prototype, see https://cmacleod.me.uk/tcl/expr_ng
This is a modified version of the prototype I wrote for tip 676. It's still in Tcl, but doesn't use `expr`. It tokenises and parses the input, then generates TAL bytecode and uses ::tcl::unsupported::assemble to run that. A few examples:
(bin) 100 % set a [= 3.0/4]
0.75
(bin) 101 % set b [= sin(a*10)]
0.9379999767747389
(bin) 102 % set c [= (b-a)*100]
18.79999767747389
(bin) 103 % namespace eval nn {set d [= 10**3]}
1000
(bin) 104 % set e [= a?nn::d:b]
1000
(bin) 105 % = {3 + [pwd]}
Calc: expected start of expression but found '[pwd]'
(bin) 106 % = {3 + $q}
Calc: expected start of expression but found '$q'
(bin) 107 % = sin (12)
-0.5365729180004349
(bin) 108 % array set rr {one 1 two 2 three 3}
(bin) 110 % = a * rr(two)
Calc: expected operator but found '('
(bin) 111 % = a * $rr(two)
1.5
- You can use $ to get an array value substituted before the `=` code sees the expression.
(bin) 112 % string repeat ! [= nn::d / 15]
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Colin.
On 02/11/2025 09:04, Donal Fellows wrote:
Doing the job properly would definitely involve changing the expression parser, with my suggested fix being to turn all bare words not otherwise recognised as constants or in positions that look like function calls (it's a parser with some lookahead) into simple variable reads (NB: C resolves such ambiguities within itself differently, but that's one of the nastiest parts of the language). We would need to retain $ support for resolving ambiguity (e.g., array reads vs function calls; you can't safely inspect the interpreter to resolve it at the time of compiling the expression due to traces and unknown handlers) as well as compatibility, but that's doable as it is a change only in cases that are currently errors.
Adding assignment is quite a bit trickier, as that needs a new major syntax class to describe the left side of the assignment. I suggest omitting that from consideration at this stage.
Donal.
-------- Original message --------
From: Colin Macleod via Tcl-Core <tcl...@li...><mailto:tcl...@li...>
Date: 02/11/2025 08:13 (GMT+00:00)
To: Pietro Cerutti <ga...@ga...><mailto:ga...@ga...>
Cc: tcl...@li...<mailto:tcl...@li...>, av...@lo...<mailto:av...@lo...>
Subject: Re: [TCLCORE] Fwd: TIP 672 Implementation Complete - Ready for Sponsorship
Indeed, this toy implementation doesn't handle that:
% = sin (12)
can't read "sin": no such variable
I'm not sure that's serious, but it could be fixed in a C implementation.
_______________________________________________
Tcl-Core mailing list
Tcl...@li...<mailto:Tcl...@li...>
https://lists.sourceforge.net/lists/listinfo/tcl-core
_______________________________________________
Tcl-Core mailing list
Tcl...@li...<mailto:Tcl...@li...>
https://lists.sourceforge.net/lists/listinfo/tcl-core
_______________________________________________
Tcl-Core mailing list
Tcl...@li...<mailto:Tcl...@li...>
https://lists.sourceforge.net/lists/listinfo/tcl-core
_______________________________________________
Tcl-Core mailing list
Tcl...@li...<mailto:Tcl...@li...>
https://lists.sourceforge.net/lists/listinfo/tcl-core
_______________________________________________
Tcl-Core mailing list
Tcl...@li...<mailto:Tcl...@li...>
https://lists.sourceforge.net/lists/listinfo/tcl-core
_______________________________________________
Tcl-Core mailing list
Tcl...@li...<mailto:Tcl...@li...>
https://lists.sourceforge.net/lists/listinfo/tcl-core
_______________________________________________
Tcl-Core mailing list
Tcl...@li...<mailto:Tcl...@li...>
https://lists.sourceforge.net/lists/listinfo/tcl-core
|
|
From: Colin M. <col...@ya...> - 2025-11-10 13:05:48
|
The next meetup will be on Tuesday 11 November 2025 at [clock format 1762887600] - Tuesday 11am US West, 1pm US Central, 2pm US East, 7pm UTC, 7pm UK, 8pm Western Europe, Wednesday 12:30am India, 3am Australia West / Singapore / China, 4am Japan, 6am Australia East, 8am New Zealand. Details (including how to connect) are available via https://wiki.tcl-lang.org/page/Monthly+Virtual+Meetup Colin. |
|
From: Colin M. <col...@ya...> - 2025-11-11 10:25:15
|
HI All, Sorry, I would not plan to do assignments or multiple calculations in the first version of [=]. I would leave these as possible enhancements for later. I do think there is less need for these features anyway if a single expression can be written concisely. I have put an updated version of my prototype at https://cmacleod.me.uk/tcl/expr_ng2. This incorporates the cache Eric introduced, but then diverges from his version somewhat. I have kept the command name as `=`, I think that's more clear than `:` to introduce a calculation. I have introduced an `extract_numbers` step before tokenisation. The point of this is to enable pre-substituted array elements e.g. $a(b) or command substitutions, e.g. [llength $list], which produce numeric values, to be used efficiently. If these are written as separate arguments (separated by spaces from the rest of the expression) then we check whether that argument already has a numeric internal representation. If so, we assign it to a temporary variable and replace it in the expression with a reference to that temporary variable. This avoids shimmering the value to/from string form. But more importantly it allows us to cache the generated bytecode, because the name of the temporary variable will stay the same although its value may change. Writing the `extract_numbers` step in Tcl is extremely kludgy, it does a regexp match on the output of tcl::unsupported::representation, ignoring the warning at https://wiki.tcl-lang.org/page/representation that this should not be used in program logic. It's also slow, though still faster than invalidating the cache. But I still see this prototype as a precursor to implementation in C, so I'm not too worried. Some timings: Without `extract_numbers`, expression without pre-substitution: (bin) 149 % timerate {set z [= z *0.9999]} 2.246294 µs/# 445176 # 445177 #/sec 999.996 net-ms Without `extract_numbers`, expression with pre-substitution: (bin) 151 % timerate {set z [= $z *0.9999]} 22.4901 µs/# 110649 # 44464.1 #/sec 2488.502 net-ms With `extract_numbers`, expression without pre-substitution: (bin) 153 % timerate {set z [= z *0.9999]} 13.6414 µs/# 73306 # 73306.1 #/sec 999.999 net-ms With `extract_numbers`, expression with pre-substitution: (bin) 154 % timerate {set z [= $z *0.9999]} 15.7187 µs/# 63618 # 63618.6 #/sec 999.990 net-ms So even with the klunky Tcl implementation the `extract_numbers` step still gives a speed-up when there is a pre-substituted numeric value. A C implementation of this logic should be much more efficient. Of course this speed-up is lost if a pre-substitution is not written as a separate argument, e.g. [= [llength $l] +2] will get the speed-up but [= [llength $l]+2] will not, so the documentation should warn about this. Colin. On 10/11/2025 07:20, Zaumseil René via Tcl-Core wrote: > > Hi Eric > > Thank you for your effort to bring this topic forward. > > Imho Colin's syntax is even better as the let version. > > I hope one of your proposals will make it into the core. > > Regards > > rene > > *Von:*EricT <tw...@gm...> > *Gesendet:* Freitag, 7. November 2025 21:35 > *An:* Zaumseil René <RZa...@kk...> > *Cc:* tc...@ro...; tcl...@li... > *Betreff:* Re: [TCLCORE] [Ext] Re: [=] for concise expressions (was > Re: TIP 672 Implementation Complete - Ready for Sponsorship) > > Rene, > > That's the beauty of making bytecode compilation official - we DON'T > have to agree on syntax! With a supported tcl::bytecode API, each of > us can create our own Design-Specific Language for our own needs. No > more "one size fits all" debates that go nowhere. > > That said, I think you might find Colin's approach already fits your > needs quite well. Here's how your examples would look: > > Multiple assignments: > > # Your let syntax > let {x 1+2 y 3*4 z $x+$y} > > # Colin's syntax (bare variables, no $) > : {x = 1+2 > y = 3*4 > z = x+y} > > > For my own taste, I've here aliased = to : since = as the command > seems to clash visually with = for assignment, especially in cases > like a = b = c = d. The semicolon requires braces since it's special > to Tcl, but by mapping newlines to semicolons internally, multiple > statements work naturally in braced blocks as shown above. > > Returning multiple values: > > # With a simple helper function > proc tcl::mathfunc::gather {args} { list {*}$args } > > # Your canvas example > .c create text {*}[= gather(x+1, y+1)] -text a > > # Or inline > = {tx = x+1 ; ty = y+1 ; gather(tx, ty)} > > > Your calculations example: > > set i 0.5 > = {x = sin(i) > y = cos(i) + x > z = x + y} > > > The key differences: bare variables (no $), explicit = for assignment, > semicolon (or newline) to separate multiple expressions, and gather() > for multiple returns. But it's all extensible - add your own functions > to tcl::mathfunc:: and they're automatically available. > > If you prefer different syntax, you can build your let command using > the same bytecode infrastructure. That's the whole point - multiple > DSLs coexisting, each optimized by bytecode caching. > > > Eric > |
|
From: da S. P. J <pet...@fl...> - 2025-11-11 13:46:52
|
I can't edit the wiki but I have to say that I really like the idea of making "set a = {expr}" a special case since it doesn't conflict with any legal code.
From: Phillip Brooks <phi...@um...>
Date: Friday, November 7, 2025 at 15:43
To: Tcl Core List <tcl...@li...>
Subject: Re: [TCLCORE] [Ext] Re: [=] for concise expressions (was Re: TIP 672 Implementation Complete - Ready for Sponsorship)
CAUTION - EXTERNAL EMAIL:
This message originated from outside of your organization. Do not click links or open attachments unless you recognize the sender and know the content is safe.
Hi all,
I started a wiki page that tries to summarize the various proposals under discussion: https://wiki.tcl-lang.org/page/A+Better+way+to+do+calculations<https://urldefense.us/v2/url?u=https-3A__wiki.tcl-2Dlang.org_page_A-2BBetter-2Bway-2Bto-2Bdo-2Bcalculations&d=DwMFaQ&c=MASr1KIcYm9UGIT-jfIzwQg1YBeAkaJoBtxV_4o83uQ&r=BRyGRggIJd8TmKOhvEmGElFuDuCl3O5mT8opva3f-Uc&m=TGv7ND_9Tnu15M7Z5KWhrcLWCUL2jQqdaUdwpmzPjmDh4DSXn21OWV7U9xfZVx5x&s=hT7sO8qd7IUadbeHgZWmIMUHP5wcGPwP2RdfHOhQj7w&e=>. Please contribute by adding to and modifying the page with concise descriptions. Let's not try to carry out debates there, but, rather, make it a clear and concise description of the alternatives that we are considering. I have sort of lost track of where we are with this right now.
Regarding the last few emails concerning exposing the tcl::unsupported::assemble command, I think the hesitancy there will be with what users do with retrieved assembly code once they have it. Will they store it and expect it to work across Tcl versions? Will they attempt to modify it and expect the modifications to work across Tcl versions? Those are very real reasons for proceeding with caution in that regard. I think we are much better off concluding a useful syntax and going with that in the Tcl core rather than trying to make this a toolkit that allows everyone to solve the underlying problem on their own.
Phil
|
|
From: Colin M. <col...@ya...> - 2025-11-11 14:02:49
|
I did consider something like that myself - perhaps `setexpr a
{expression}`. But that does not help with writing a simple expression
as one of the arguments to another command. On the other hand, if you
have [= expression] then you can easily write `set a [= expr] which is
just as short, so a new [=] command helps both cases.
Colin.
On 11/11/2025 13:46, da Silva, Peter J wrote:
> I can't edit the wiki but I have to say that I really like the idea of
> making "set a = {expr}" a special case since it doesn't conflict with
> any legal code.
>
> *From: *Phillip Brooks <phi...@um...>
> *Date: *Friday, November 7, 2025 at 15:43
> *To: *Tcl Core List <tcl...@li...>
> *Subject: *Re: [TCLCORE] [Ext] Re: [=] for concise expressions (was
> Re: TIP 672 Implementation Complete - Ready for Sponsorship)
>
> CAUTION - EXTERNAL EMAIL:
> This message originated from outside of your organization. Do not
> click links or open attachments unless you recognize the sender and
> know the content is safe.
> Hi all,
> I started a wiki page that tries to summarize the various proposals
> under discussion:
> https://wiki.tcl-lang.org/page/A+Better+way+to+do+calculations
> <https://urldefense.us/v2/url?u=https-3A__wiki.tcl-2Dlang.org_page_A-2BBetter-2Bway-2Bto-2Bdo-2Bcalculations&d=DwMFaQ&c=MASr1KIcYm9UGIT-jfIzwQg1YBeAkaJoBtxV_4o83uQ&r=BRyGRggIJd8TmKOhvEmGElFuDuCl3O5mT8opva3f-Uc&m=TGv7ND_9Tnu15M7Z5KWhrcLWCUL2jQqdaUdwpmzPjmDh4DSXn21OWV7U9xfZVx5x&s=hT7sO8qd7IUadbeHgZWmIMUHP5wcGPwP2RdfHOhQj7w&e=>.
> Please contribute by adding to and modifying the page with concise
> descriptions. Let's not try to carry out debates there, but,
> rather, make it a clear and concise description of the alternatives
> that we are considering. I have sort of lost track of where we are
> with this right now.
>
> Regarding the last few emails concerning exposing the
> tcl::unsupported::assemble command, I think the hesitancy there will
> be with what users do with retrieved assembly code once they have it.
> Will they store it and expect it to work across Tcl versions? Will
> they attempt to modify it and expect the modifications to work across
> Tcl versions? Those are very real reasons for proceeding with caution
> in that regard. I think we are much better off concluding a useful
> syntax and going with that in the Tcl core rather than trying to make
> this a toolkit that allows everyone to solve the underlying problem on
> their own.
>
> Phil
>
>
> _______________________________________________
> Tcl-Core mailing list
> Tcl...@li...
> https://lists.sourceforge.net/lists/listinfo/tcl-core |
|
From: Donal F. <don...@ma...> - 2025-11-03 08:23:46
|
Doing the job properly would definitely involve changing the expression parser, with my suggested fix being to turn all bare words not otherwise recognised as constants or in positions that look like function calls (it's a parser with some lookahead) into simple variable reads (NB: C resolves such ambiguities within itself differently, but that's one of the nastiest parts of the language). We would need to retain $ support for resolving ambiguity (e.g., array reads vs function calls; you can't safely inspect the interpreter to resolve it at the time of compiling the expression due to traces and unknown handlers) as well as compatibility, but that's doable as it is a change only in cases that are currently errors. Adding assignment is quite a bit trickier, as that needs a new major syntax class to describe the left side of the assignment. I suggest omitting that from consideration at this stage. Donal. -------- Original message -------- From: Colin Macleod via Tcl-Core <tcl...@li...> Date: 02/11/2025 08:13 (GMT+00:00) To: Pietro Cerutti <ga...@ga...> Cc: tcl...@li..., av...@lo... Subject: Re: [TCLCORE] Fwd: TIP 672 Implementation Complete - Ready for Sponsorship Indeed, this toy implementation doesn't handle that: % = sin (12) can't read "sin": no such variable I'm not sure that's serious, but it could be fixed in a C implementation. |