Набор тестов ANS Forth Джона Хейса содержит следующее определение:
: IFFLOORED [ -3 2 / -2 = INVERT ] LITERAL IF POSTPONE \ THEN ;
Затем это используется для условного определения различных слов в зависимости от того, используем ли мы напольное или симметричное деление:
IFFLOORED : T/MOD >R S>D R> FM/MOD ;
Таким образом, IFFLOORED
действует либо как noop, либо как \
в зависимости от результата выражения. Отлично. Это легко реализовать в моем многопоточном интерпретаторе, выполнив следующее:
: POSTPONE ' , ; IMMEDIATE
...и теперь IFFLOORED
работает; определение эквивалентно : IFFLOORED -1 IF ['] \ EXECUTE THEN ;
.
К сожалению, дальше по набору тестов находится следующий код:
: GT1 123 ;
: GT4 POSTPONE GT1 ; IMMEDIATE
: GT5 GT4 ;
\ assertion here that the stack is empty
Та же реализация здесь не работает. Если POSTPONE
компилирует ссылку на свое слово, то GT4
становится эквивалентом : GT4 123 ;
... но GT4
является немедленным. Итак, когда GT5
определено, 123 помещается в стек компилятора, а GT5
становится noop. Но это неправильно; набор тестов ожидает, что вызов GT5
оставит 123 в стеке. Итак, чтобы это работало, POSTPONE
должен генерировать код, который генерирует код:
: POSTPONE ' LITERAL ['] , LITERAL ;
И действительно, если поиграться с gForth, то я вижу, что POSTPONE
на самом деле работает так:
: GT1 123 ;
: GT4 POSTPONE GT1 ; IMMEDIATE
SEE GT4
<long number> compile, ;
Но эти два определения несовместимы. Если я использую второе определение, первый тест завершится неудачей (поскольку теперь IFFLOORED
пытается скомпилировать \
, а не выполнить его). Если я использую первое определение, второй тест завершится ошибкой (поскольку GT4
вставляется в стек компилятора, а не компилируется буквально).
...но оба теста проходят в gForth.
Так что же происходит?