I'm trying to implement a kind of "conditional :
" in ATLAST, the reasoning being I have a file that gets FLOAD
ed multiple times to handle multiple steps of my program flow (I'm essentially abusing Forth as an assembler, step 1 does a first parsing for references, etc. and in step 2 the instruction words actually emit bytes).
So when declaring words for "macros" in that file, it errors out in step 2, because they were already declared in step 1, but I also can't just FORGET
them, because that would forget everything that came afterwards, such as the references I just collected in step 1.
So essentially I need a ":
that only runs in step 1", my idea being something like this:
VARIABLE STAGE
: ::
STAGE @ 0 = IF
[COMPILE] : ( be a word declaration )
EXIT
THEN
BEGIN ( eat the disabled declaration )
' ( get the address of the next word )
['] ; ( get the address of semicolon )
= ( loop until they are equal )
UNTIL
; IMMEDIATE
:: FIVE 5 ; ( declares as expected )
FIVE . ( prints 5 )
1 STAGE ! ( up to here everything's fine )
:: FIVE 6 ; ( is supposed to do nothing, but errors out )
FIVE . ( is supposed to print 5 again )
The traced error message (starting from 1 STAGE !
):
Trace: !
Trace: ::
Trace: STAGE
Trace: @
Trace: (LIT) 0
Trace: =
Trace: ?BRANCH
Trace: '
Trace: (LIT) 94721509587192
Trace: =
Trace: ?BRANCH
Trace: '
Word not specified when expected.
Trace: ;
Compiler word outside definition.
Walkback:
;
KEY ( -- ch )
as common in some other Forths for reading a single character from the input stream ( outside the ::
declaration, since it's IMMEDIATE
) doesn't exist in ATLAST, the only related words I could find are:
'
: is supposed to read a word from the input stream, then pushes its compile address[']
: like '
but reads a word from the current line (the inside of the ::
declaration)(LIT)
/(STRLIT)
: are supposed to read literals from the input stream according to the documentation, I could only ever make them segmentation fault, I think they're for compiler-internal use only (e.g., if the compiler encounters a number literal it will compile the (LIT)
word to make it push that number onto the stack)There aren't any WORD
or PARSE
either, as in some other Forths.
As you can see, '
is struggling actually getting something from the input stream for some weird reason, and it looks like [']
is failing to capture the ;
which then errors out because it's suddenly encountering a ;
where it doesn't belong.
I suspect it actually ran ' [']
, even though it's supposed to work on the input stream, not the immediate line, and I'm clearly in compile mode there.
I did a similar thing with conditionally declaring variables, there it was rather easy to just [COMPILE] ' DROP
to skip a single word (turning RES x
into ' x DROP
), but here I'm pretty sure I can't actually compile those instructions, because I can't emit a loop outside of a declaration. Unless there is a way to somehow compile similar code that recursively gets rid of everything until the ;
.
A problem is that '
cannot find a number. A possible solution is to use a special dummy name for the definition, instead of skip it over:
: ::
STAGE @ 0 = IF : EXIT THEN
' DROP \ this xt isn't needed
" : _dummy" EVALUATE ( -- n ) DROP
;
Or maybe use a new name every time:
: ::
STAGE @ 0 = IF : EXIT THEN
' >NAME @ \ ( s1 ) \ should be checked
": _dummy_" DUP >R S+
R> EVALUATE ( -- n ) DROP
;
But due to non standard words it might not work. Another problem is that non colon-definitions are out of the scope.
Perhaps, a better solution is a preprocessing by external means.