Идея в следующем:
Вводим переменную:
0 USER EXIT-POINTER
Определяем слово, которое будет компилироваться в начало каждого определения через двоеточие:
: (:) R> EXIT-POINTER @ >R RP@ EXIT-POINTER ! >R ;
Это слово сохраняет предыдущий указатель стека возвратов и запоминает новый, который на входе в определение.
Определяем слово, которое будет восстанавливать состояние стека возвратов:
: (EXIT) ( -- ) EXIT-POINTER @ RP! R> EXIT-POINTER ! ;
Теперь вводим определяющие слова, которые будут создавать определения через двоеточие.
В SimplyForth это будет выглядеть так:
DEFER OLD-: IMMEDIATE
' : IS OLD-:
OLD-: : ( -- )
?EXEC HEADER !CSP 0 COLON-ID >CS POSTPONE (:) ] ; IMMEDIATE
OLD-: :NONAME ( -- cfa )
?EXEC !CSP HERE NONAME-ID >CS POSTPONE (:) ] ; IMMEDIATE
OLD-: ; ( -- )
?COMP CS> DUP COLON-ID = IF 2DROP ELSE NONAME-ID ?PAIRS THEN
POSTPONE (EXIT) POSTPONE RECURSIVE POSTPONE [ ?CSP ; IMMEDIATE
В других Форт-системах определения будут другими, но тут самое главное — это
POSTPONE (:)
и POSTPONE (EXIT)
.
Ну и сам EXIT:
: EXIT ( -- )
?COMP POSTPONE (EXIT) ; IMMEDIATE
Все, теперь EXIT не зависит от состояния стека возвратов и ему не нужен UNLOOP.
Важно помнить, что теперь в определениях через двоеточие на вершине стека возвратов находится не адрес возврата, а указатель предыдущего состояния стека возвратов, а адрес возврата находится под ним.