[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Attempt at implementing labeled loops
From: |
JFLF |
Subject: |
Re: Attempt at implementing labeled loops |
Date: |
Wed, 3 Feb 2021 22:43:22 +0100 |
User-agent: |
Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.7.0 |
Hello Anton,
Thank you very much for your answer! It clarifies a lot of thing, in particular
the definition-within-a-definition that I was attempting. My mistake.
The real goal that I was trying to achieve (and which I finally solved with
CS-PICK), is a command line argument parser:
: 1continue ( dest -- dest ) \ compilation
1 cs-pick postpone again
; immediate
\ Parse command line arguments
: parseargs ( -- f )
begin
next-arg 2dup 0 0 d<>
while
\ switch case over strings
2dup s" -h" str= if 2drop false exit then
2dup s" -p" str= if 2drop true to pp? 1continue then
2dup s" -1" str= if 2drop true to 1only? 1continue then
\ nothing matched -> it's a wrong parameter
." ERROR: unknown command line parameter: " type cr false exit
repeat
true \ no error, signal it to caller
;
It works but I am not happy with the hackiness of it.
I could use a CASE switch between WHILE and REPEAT, using ?OF for a generic
test which would be a lot cleaner, but GForth 0.7.3 doesn't support it (or at
least the version in the Debian repos). NEXT-CASE isn't there either, nor
CONTOF.
I was probably being overzealous with the labeled loops, although I still think
that in absolute terms it would be nice syntactic sugar:
begin: loop1
begin: loop2
loop2 break
loop1 again ( from within loop2 )
etc.
Thanks again!
JF
On 03/02/2021 19.02, Anton Ertl wrote:
> On Wed, Feb 03, 2021 at 05:01:28PM +0100, JFLF wrote:
>>
>> Hello all,
>>
>> Apologies if this is not the right place to ask. I have been attempting for
>> a few days to solve a specific problem with GForth 0.7.3, but so far I have
>> failed. Could anyone provide some advice? Disclaimer: I'm still learning
>> Forth.
>>
>> I have been trying to implement something akin to continue in C loops, but
>> with labels. Ideally I'd like to achieve this:
>>
>> *: begin looplabel: loop1**
>> **
>> ** <condition>**
>> ** while**
>> ** ...**
>> ** <test> if loop1 again then**
>> ** ...**
>> ** repeat**
>> **;*
>>
>>
>> Essentially I'm looking at replacing *[ x cs-pick ] again* by something a
>> bit more manageable, especially with nested control-flow items.
>
> You may be interested in the word CONTOF (which ends an OF or ?OF part
> in a CASE structure) in the development version of Gforth. Your
> example (if I understand it) would become
>
> case
> <condition> ?of endof
> ...
> <test> ?of contof
> ...
> next-case
>
>> Side note: the way *while* tucks its orig cf item under *begin*'s dest also
>> caused me quite a bit a trouble, as it disrupts the obvious index
>> progression of cf items on the cf stack. It's defined as such in the
>> standard, but does it make sense?
>
> Yes, it allows doing things like
>
> begin
> ... while
> ... while
> ... while
> ...
> again then then then
>
>> My current implementation and a test word look like this:
>>
>> *: looplabel:
>>
>> create
>> 2 pick , 2dup 2,
>> does> immediate
>> dup @ swap cell+ 2@
>> ; immediate*
>>
>>
>> *: testbegin ( -- )
>>
>> 3
>> begin looplabel: loop2
>> 1- dup 0<>
>> while
>> dup 2 = if ." IF taken" cr loop2 again then
>> ." After the test: " dup . cr
>> repeat
>> drop
>> ;*
>
> You are trying to CREATE LOOP2 in the middle of the code for
> TESTBEGIN; that's not going to work. The development version has some
> support for this kind of stuff, but I don't think we have documented
> it completely yet.
>
>> Dumping the control stack at compilation time (with additional
>> instrumentation in the *testbegin* word), things /seem/ to be fine. For
>> example:
>>
>> *Before begin: <4> 0 140421634250120 140421634250152 0
>> After begin <7> 0 140421634250120 140421634250152 0 0 140421634250184
>> 3
>> After llabel: <7> 0 140421634250120 140421634250152 0 0 140421634250184
>> 3
>> loop2 0 140421634250184 3
>> After while <10> 0 140421634250120 140421634250152 0 0
>> 140421634250408 1 0 140421634250184 3
>> After if <13> 0 140421634250120 140421634250152 0 0
>> 140421634250408 1 0 140421634250184 3 0 140421634250456 1
>> After loop2 <16> 0 140421634250120 140421634250152 0 0
>> 140421634250408 1 0 140421634250184 3 0 140421634250456 1 0 140421634250184 3
>> After again <13> 0 140421634250120 140421634250152 0 0
>> 140421634250408 1 0 140421634250184 3 0 140421634250456 1
>> After then <10> 0 140421634250120 140421634250152 0 0
>> 140421634250408 1 0 140421634250184 3
>> After repeat <4> 0 140421634250120 140421634250152 0 *
>>
>>
>> But any attempt at executing *testbegin* gives that kind of result:
>>
>> *testbegin
>> :2: Invalid memory address
>> >>>testbegin<<<
>> Backtrace:
>> $7FF9C41F95C0 lit *
>
> That's probably because the header and body of LOOP2 is in the middle
> of TESTBEGIN. I am surprised that TESTBEGIN is REVEALed at all.
>
>> The exact error changes, I have seen some stack underflows for example.
>>
>> *see*-ing the words includes a few surprises:
>>
>> *see looplabel: *
>> *: looplabel: *
>> * Create 2 pick , 2dup 2, 140710713988408 (does>2) ; immediate ok*
>>
>> *see testbegin *
>> *noname : *
>> * 3 *
>> * BEGIN BEGIN <140710713988240> <-4611686018427387899>
>> <2314885609475239788> <94220049618193> <140710713988424> <0> <3>
>> <140710713988552> .\" In begin: TOS " dup . cr 1- dup 0<> *
>> * WHILE dup 2 = *
>> * WHILE .\" IF taken" cr *
>> * REPEAT *
>> * .\" After the test" cr *
>> * REPEAT *
>> * drop ; ok*
>
> The numbers in <...> are the header and body of LOOP2.
>
>> Replacing *looplabel:* by *cs-pick* produces the right results, but *see*
>> still looks weird:
>>
>> *: testbegin2 ( -- )
>>
>> cr 3
>> begin
>> 1- dup 0<>
>> while
>> dup 2 = if ." IF taken" cr [ 1 cs-pick ] again then
>> ." After the test: " dup . cr
>> repeat
>> drop
>> ;*
>>
>>
>> *testbegin2
>> IF taken
>> After the test: 1
>> ok*
>>
>>
>> *see testbegin2
>> noname :
>> cr 3
>> BEGIN BEGIN 1- dup 0<>
>> WHILE dup 2 =
>> WHILE .\" IF taken" cr
>> REPEAT
>> .\" After the test: " dup . cr
>> REPEAT
>> drop ; ok*
>
> The decompiler tries to reconstruct the control structure from the
> branches that the code is compiled to. This decompiler result gives
> an idea of how this could be coded without CS-PICK. Thinking through
> it, this is indeed the loop you wanted: The CS-PICK is replaced by
> having a second BEGIN (in the same place), the first WHILE branches
> after the second REPEAT, the second WHILE after the first REPEAT. and
> both REPEATs branch back to the BEGINs.
>
>> So here are my questions:
>>
>> 1) I feel that I am missing some compile-time side effect of the
>> *looplabel:* word but after two days of going through the GForth doc I can't
>> figure out what. Any hint?
>
> LOOPLABEL produces stuff in the dictionary. The threaded code of
> TESTBEGIN also resides in the dictionary; so you get the LOOP2 stuff
> in the middle of the threaded code, which leads to breakage.
>
> If you want to go ahead with named labels, a way to do it would be to
> define it outside the colon definition, and then use it inside, maybe
> like:
>
> : looplabel:
> create 0 , immediate
> does> here swap ! ;
>
> : goto
> postpone branch ' >body @ , ; immediate
>
> LOOPLABEL: LOOP2:
>
> : testbegin ( -- )
> begin loop2:
> 1- dup 0<>
> while
> dup 2 = if ." IF taken" cr goto loop2: then
> ." After the test: " dup . cr
> repeat
> drop
> ;
>
>> 2) In both test words, the nested *if* compiles as a second *begin while
>> repeat*. Why is that?
>
> WHILE and IF compile a conditional branch forward.
>
> REPEAT is AGAIN THEN and compile an unconditional branch backwards
> followed by a branch target.
>
> So you can write it either way, and get the same result. And the
> decompiler then guesses at how to decompile it.
>
>> 4) Ideally I would want the scope of those loop labels to be strictly
>> limited to the current word definition. I thought of locals, but I believe
>> that they're still visible in called words, right?
>
> No.
>
>> Is there a mechanism to limit the scope of locals in GForth?
>
> SCOPE ... ENDSCOPE
>
> But that's for limiting the scope within a colon definition.
>
> - anton
>