В-четвертых: ОТЛОЖИТЬ, как это работает?

Набор тестов 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.

Так что же происходит?


person David Given    schedule 26.07.2015    source источник


Ответы (2)


Позвольте мне ответить здесь, поскольку вопрос значительно изменился. Я все еще не уверен, что понял вопрос, хотя :)

В вашем примере вы определяете

: GT4 POSTPONE GT1 ; IMMEDIATE

Здесь происходит следующее:

  1. : выполняется, считывается GT4 и создается новое слово
  2. Выполняется семантика компиляции POSTPONE, которая заключается в компиляции семантики компиляции GT1 - как вы видели в GForth.
  3. ; выполняется, заканчивая определение
  4. IMMEDIATE выполняется, помечая последнее определенное слово как непосредственное.

POSTPONE вызывается только при компиляции GT4 и не отображается в скомпилированном коде. Поэтому при последующем использовании этого непосредственного слова в определении GT5 семантика интерпретации POSTPONE не требуется.

Кстати, согласно стандарту, POSTPONE имеет только компиляцию семантика, а семантика интерпретации не определена.

См. также руководство POSTPONE в руководстве по GForth.

EDIT Примеры семантики интерпретации и компиляции:

: TEST1 ." interpretation" ;  => ok
: TEST2 ." compilation" ; IMMEDIATE  => ok
: TEST3 TEST1 TEST2 ;  => compilation ok
TEST3  => interpretation ok
: TEST4 POSTPONE TEST1 ; IMMEDIATE  => ok
: TEST5 TEST4 ;  => ok
TEST5  => interpretation ok
: TEST6 POSTPONE TEST2 ; IMMEDIATE  => ok
TEST6  => compilation ok

Если у вас есть еще вопросы, вы можете сослаться на эти тесты.

person vukung    schedule 26.07.2015
comment
Да я все это знаю! Чего я не понимаю, так это того, как POSTPONE может работать в этом случае и в случае IFFLOORED. GT4 становится словом, которое при вызове компилирует вызов GT1. Но IFFLOORED оказывается словом, которое при вызове выполняет вызов \. Как POSTPONE может делать и то, и другое? - person David Given; 26.07.2015
comment
POSTPONE всегда компилирует семантику компиляции слова. В случае GT1 его семантика компиляции заключается в том, чтобы скомпилировать вызов GT1. В то время как в случае \, являющегося словом IMMEDIATE, его семантика компиляции состоит в том, чтобы рассматривать остальную часть строки как комментарий. Итак, по сути, POSTPONE откладывает выполнение следующего слова — если оно немедленное, то компилирует простой вызов к нему; если это будет простой вызов, то он компилирует код для его компиляции. - person vukung; 26.07.2015
comment
О, он смотрит, чтобы увидеть, что это за слово! Это имеет смысл (и как только я сделал реализацию, она даже работает). Спасибо! - person David Given; 26.07.2015

Фрагмент, который вы цитируете, делает следующие вещи:

  1. Оцените -3/2 (во время компиляции) и проверьте, равно ли оно -2.
  2. Если это так, сохраните 0 (ложь), в противном случае сохраните -1 (истина) в IFFLOORED, поэтому при оценке оно поместит это значение в стек. (Это эффект LITERAL.)
  3. При вычислении IFFLOORED после помещения значения в стек появляется выражение IF - THEN. Когда значение равно true, это означает, что мы не находимся в окружении пола, поэтому мы хотим закомментировать оставшуюся часть строки, что и делает \.

Итак, здесь начинается сложная часть — \ это IMMEDIATE, т. е. вы не можете использовать его внутри определения с двоеточием, потому что остальная часть строки будет закомментирована. Вы должны явно указать компилятору, что хотите скомпилировать эту функцию, а не выполнять ее, что и делает POSTPONE.

person vukung    schedule 26.07.2015
comment
Извините, вы совершенно не поняли суть моего вопроса --- я понимаю, как работает IFFLOORED; это POSTPONE меня озадачивает. Я обновил вопрос, чтобы сделать его, надеюсь, яснее именно то, что я хочу знать. - person David Given; 26.07.2015