fpjohnston / TECO-64

Enhanced and portable version of TECO text editor in C.
24 stars 6 forks source link

Control structure works differently from traditional TECO #22

Open LdBeth opened 3 months ago

LdBeth commented 3 months ago

This might be more of a feature request than bug report.

A interesting phrase I found in squ.tes has interleaved loop and conditional constructs

< D .-Z; 0A"D > '

and the effect is delete digits from current point until there is no more. (although the ?UTC would be thrown at run time if noun digit is encountered in TECOC)

Apparently the traditional TECO treats control flow commands as anchor points to go to, where TECO-64 works differently.

Consulting the Standard TECO user guide, I was wondering the lack of unconditional exit from loop construct, and if interleave loop with conditional is allow then I would not be surprised the "intentional" omitting of such construct.

LdBeth commented 3 months ago

With minor modification this works with TECO-64

< D .-Z; 0A "D | 1; ' >
rhaberkorn commented 3 months ago

Apparently the traditional TECO treats control flow commands as anchor points to go to, where TECO-64 works differently.

"Traditional" TECO treats control flow commands like gotos, just that the jump address/label is determined by some primitive string scanning, relying on no other state. That's why code like this is "safe". In SciTECO, where I have a bit more modern approach to flow control constructs - it does actually drive the parser when looking for its "jump points" and tries to hash them - the construct you quoted will work, but in the exit-case leave the loop open.

rhaberkorn commented 3 months ago

Although there must have been some state (stack?) to support nested n<...> constructs. So this might not be entirely clean code, even on "classic" TECOs.

LdBeth commented 3 months ago

Although there must have been some state (stack?) to support nested n<...> constructs. So this might not be entirely clean code, even on "classic" TECOs.

What I imaging could be used to handle counted loop is allocate an integer cell for each n< in the macro, and do not worry about free the array until macro execution for the level is completed, because the storage won't grow as program runs. when ever the flow of execution encounters n<, search the corresponding cell and set the number to n, and whenever encounters > search cell for matching <, and either exits when the counter is 0 or decrement the count and go to the position after n<.

rhaberkorn commented 3 months ago

As I suspected, both TECO for Ultrix and TECOC also use a loop stack internally to keep track of open loops. When repeating a loop they therefore also do not need to scan for < but perform an immediate jump. And there is hardly any other way you could implement it. This means that the construct @LdBeth cited should not be considered a canonical way to exit a loop. Because in fact it does NOT exit it. Instead you are practically leaking memory. Also, if you had loops enclosing this construct, you would find that the next > jumps back into the loop, you supposedly already closed. This is all not relevant for squ.tes.

LdBeth commented 3 months ago

Instead you are practically leaking memory. Also, if you had loops enclosing this construct, you would find that the next > jumps back into the loop, you supposedly already closed.

If the implementation keeps track of matched open < and closing > when restart a loop from >, this should not be a problem. ITS TECO even has ; working by tracks < and > in the label, find the first unpaired '>' after it, so that !<!> insides command loop jumps to beginning of the loop, while the !<! is used to help ; correctly identifies end of loop, because it took F< for a totally different meaning.

rhaberkorn commented 3 months ago

If the implementation keeps track of matched open < and closing > when restart a loop from >, this should not be a problem

But they don't. TECO-interpreters do not build syntax trees. And if they did, constructs like the ones used in squ.tes wouldn't be allowed. Because it would be similar to (pseudocode):

while (...) {
    ...
    if is_digit() then }

TECO is not a "typical" language. The code used in your example should still execute in TECO-64 probably, but you should be aware what you are doing.

I think what you mean by your ITS remarks is the same behavior that all TECO interpreters have - except for SciTECO. Some flow control commands will just do a string scan to find their jump destination, requiring you to sometimes add !<! so that a following > is ignored by the primitive scanning algorithms (it sees balanced braces). Didn't know they even relied on this idiom to replace F<!

LdBeth commented 3 months ago

But they don't. TECO-interpreters do not build syntax trees. And if they did, constructs like the ones used in squ.tes wouldn't be allowed. Because it would be similar to (pseudocode):

Right, that's what I'm wondering is there any special implementation tricks used in the "standard" TECO can get interleaved loop working without get signaled error. It certainly makes sense for languages like HP SystemRPL which can directly manipulates control stream to implement control structures like loops and conditionals.

Although it looks to me the squ macro from TECOC is also copied from somewhere else...

LdBeth commented 3 months ago

While using interleaved loop construct can be controversial, what now more bothers me is

test1 "E
test2 "E
test3 "E
(test1 && test2 %% test3)
'

or

^AEcho^A | ^Aignored^A '

which should be considered as valid according to what's in the TECO manual (and does not raise error in TECO C) gives parse error.

LdBeth commented 3 months ago

And some notes related to F| and F': they are quite useless if cannot be used inside command loop

0 "E < 1; F| > | @^A/else/ ' ! print nothing, no error reported !

0 "E < -1; F| > | @^A/else/ ' ! print "else", teco-64 reports ?BNI !

That basically renders F| and F' incapable for computed jumps, because they cannot escape multiple layers of conditions.