Search code examples
forthgforth

New lines in word definition using interpreter directives of Gforth


I am using the interpreter directives (non ANS standard) control structures of Gforth as described in the manual section 5.13.4 Interpreter Directives. I basically want to use the loop words to create a dynamically sized word containing literals. I came up with this definition for example:

: foo
   [ 10 ] [FOR]
      1
   [NEXT]
   ;

Yet this produces an Address alignment exception after the [FOR] (yes, I know you should not use a for loop in Forth at all. This is just for an easy example).

In the end it turned out that you have to write loops as one-liners in order to ensure their correct execution. So doing

: foo [ 10 [FOR] ] 1 [ [NEXT] ] ;

instead works as intended. Running see foo yields:

: foo  
    1 1 1 1 1 1 1 1 1 1 1 ; ok

which is exactly what I want.

Is there a way to get new lines in the word definition? The words I would like to write are way more complex, and for a presentation I would need them better formatted.


Solution

  • It would really be best to use an immediate word instead. For example,

    : ones ( n -- ) 0 ?do 1 postpone literal loop ; immediate
    : foo ( -- ten ones )  [ 10 ] ones ;
    

    With SEE FOO resulting in the same as your example. With POSTPONE, especially with Gforth's ]] .. [[ syntax, the repeated code can be as elaborate as you like.

    A multiline [FOR] would need to do four things:

    1. Use REFILL to read in subsequent lines.

    2. Save the read-in lines, because you'll need to evaluate them one by one to preserve line-expecting parsing behavior (such as from comments: \ ).

    3. Stop reading in lines, and loop, when you match the terminating [NEXT].

    4. Take care to leave >IN right after the [NEXT] so that interpretation can continue normally.

    You might still run into issues with some code, like code checking SOURCE-ID.

    For an example of using REFILL to parse across multiple lines, here's code from a recent posting from CLF, by Gerry:

    : line,  ( u1 caddr2 u2 -- u3 )
        tuck here swap chars dup allot move +
    ; 
    
    : <text>  ( "text" -- caddr u )
        here 0
        begin
            refill
        while
            bl word count s" </text>" compare
        while
            0 >in ! source line, bl c, 1+
        repeat then
    ;
    

    This collects everything between <text> and a </text> that's on its own line, as with a HERE document, while also adding spaces. To save the individual lines for [FOR] in an easy way, I'd recommend leaving 0 as a sentinel on the data stack and then drop SAVE-MEM 'd lines on top of it.