[an error occurred while processing this directive]

.цв

3.2. Векторные слова

.нф

Операции, выполняемые словами Форта, обычно фиксируются во время их определения. Слово может быть переопределено впоследствиии, с этого момента все ссылки на него будут использовать это новое определение. Однако все прежние ссылки будут опираться на старое значение слова. Было бы полезно иметь простой метод изменения определения таким путем, чтобы все предыдущие словарные статьи также переключились бы на новую версию слова. Одним из методов достижения этого служит использование исполнительных векторов.

Идею исполнительного вектора лучше всего объяснить на примере. Пусть мы определили новые слова

: ICAN  ." Я могу сделать " ;
: 1STWORD  ICAN ." это" ;
: 2NDWORD  ." Или " ICAN ." что-то иное" ;

Теперь мы определим слово DEMO, которое исполняет 1STWORD. Причем воспользуемся не определением через двоеточие, а косвенным путем - через содержимое переменной:

VARIABLE ACTION
' 1STWORD CFA ATION !

Теперь ACTION содержит исполнительный адрес слова 1STWORD, так что DEMO можно записать так:

: DEMO ACTION @ EXECUTE ;

Исполнение DEMO выдаст на экран

   Я могу сделать это

Цель такого окольного пути решения задачи в том, что так мы можем изменить действие DEMO, просто изменив содержание переменной ACTION, например исполнение:

' 2NDWORD CFA ACTION !

приведет к тому, что DEMO будет выдавать

   Или Я могу сделать что-то иное

Таким образом DEMO будет исполнять действия любых других слов, чей исполнительный адрес будет помещен в переменную ACTION. Такая переменная известна как исполнительный вектор для DEMO.

Этот прием очень полезен, но имеет недостаток: он неэффективно расходует память, так как требует 3 объекта для каждой векторной процедуры: само векторное слово (DEMO), исполнительный вектор (ACTION) и процедуру его инициализации (' 2NDWORD CFA ACTION !).

В Форт-Агате эти недостатки устранены. В нем можно создавать слова, назначением которых является исполнение присвоенного им слова Форта. Это своебразные переменные, содержимым которых являются другие слова Форта. Основное назначение этих слов - изменять выполняемые действия слов Форта уже после того, как они были скомпилировны в словарь.

 Эти слова, называемые векторными, образуются словом EXVEC: с последующим именем:
              EXVEC: <ИМЯ>

Эта команда создает векторное слово <ИМЯ> со структурой словарной статьи, напоминающей структуру переменной: в ее поле кода указан адрес исполнительной части определяющего слова EXVEC:, а в поле параметров, состоящем из одной ячейки, - адрес поля кода исполняемого слова. При образовании векторного слова в это поле записывается адрес поля кода слова NOVEC, которое выводит на экран сообщение об ошибке "Исполнительный вектор не определен", предотвращая тем самым разрушение Форт-системы при случайном исполнении этого слова. Для инициализации векторного слова, нужно присвоить ему какое-либо слово Форта из словаря. Для этого исполняют выражение:

         ASSIGN <ИМЯ> TODO <ИМЯ2>

Теперь при исполнении слова <ИМЯ> будет выполняться слово <ИМЯ2>. По-существу, происходит переименование слова <ИМЯ2> в слово <ИМЯ>. Конечно, такое переименование можно выполнить проще, используя определение через двоеточие:

              : <ИМЯ> <ИМЯ2> ;

но векторное слово удобнее: присвоение значения ему можно выполнить внутри словарной статьи и можно многократно повторять без изменения словаря, чего нельзя сделать с определением через двоеточие, а, главное, можно переопределять слова основного словаря в зависимости от потребностей пользователя.

Вернемся к нашему примеру. Вначале определим векторное слово:

EXVEC: DEMO

Затем зададим исполнительный вектор созданного слова:

ASSIGN DEMO TODO 2NDWORD

И мы получили векторное слово DEMO с теми же функциями, что и раньше. В любое время можно переназначить DEMO для исполнения другого слова.

В основном словаре Форта имеется несколько векторных слов. Они представлены ниже вместе со словами, которые они исполняют после холодного старта Форт-системы.

        Векторное слово   Исполнительный вектор
            ABORT               (ABORT)
            CREATE              (CREATE)
            EMIT                (EMIT)
            KEY                 (KEY)
            MESSAGE             ¤MSG
            NUM                 (NUM)
            R/W                 brw (слово без заголовка)
            UPDATE              (UPDATE)

Хотя эти слова находятся в ядре Форта, их можно легко переопределить. Например, переопределим слово EMIT, которое по умолчанию выдает на экран символ нажатой клавиши. Заставим это слово печатать на экране управляющий символ (т.е. символ с кодом от 129 до 159) и одновременно исполнять его:

: ^EMIT DUP 160 < OVER 128 > AND IF DUP (EMIT)
         64 + ." ^" THEN (EMIT) ;
 ASSIGN EMIT TODO ^EMIT

После интерпретации этих двух строк все управляющие коды будутне только исполняться, но и печататься в виде латинских букв с приставкой ^, напр., "звонок" (УПР/G) - как ^G.

Нормальное действие этих клавиш можно вернуть, исполнив COLD или, что менее кардинально, ASSIGN EMIT T0DO (EMIT).

Способность к переопределению процедуры MESSAGE сообщений об ошибках позволяет создать свою систему сообщений об ошибках.

Приведем 2 варианта.

Первый вариант основан на записи текстовых сообщений об ошибках на листах 1 и 2 и печати их на экране слово .LINE:

: ERRMESS  ?DUP IF 16 /MOD 1+ .LINE THEN ;
 ASSIGN MESSAGE TODO ERRMESS

Второй метод основан на хранении текстов сообщений в словаре. Для этого применим оператор CASE:, который определим как

: CASE: CREATE SMUDGE ] DOES> OVER + + @EXECUTE ;

Каждое сообщение напишем в виде определения через двоеточие:

: ESTK ." Стек пуст" ;
: DFUL ." Словарь полон" ;
: AMOD ." Неверен адрес режима" ;
: REDF ." Уже есть" ;
: PARA ." Параметр вне границ" ;
: SCR# ." Такого листа нет" ;
: FSTK ." Стек полон" ;
: EFIL ." Файл открыть нельзя" ;
: R/WE ." Ошибка чтения/записи" ;
: EOLN ." Конец строки? " ;
: /ZER ." Деление на 0" ;
: EVEC ." Вектор на определен" ;
: EBRN ." Ветвление длинно" ;
: CURV ." Неверен текущий список" ;
: COMP ." Только при компиляции" ;
: EXEC ." Только при исполнении" ;
: COND ." Непарные условия" ;
: DEFN ." Незавершено определение" ;
: PROT ." Защищено от стирания" ;
: LODG ." Не при LOAD" ;
: EDSC ." Вне текущего листа" ;
: CRNT ." Нет в текущем списке" ;
: EMEM ." Нет памяти" ;
 CASE: ERRMESS
NOOP ( ничего не сообщается при ошибке 0 )
ESTK DFUL AMOD REDF PARA SCR# FSTK FILE R/WE EOLN /ZER
EVEC EBRN CURV NOOP NOOP COMP EXEC COND DEFN PROT LODG
EDSC CRNT EMEM ;
 ASSIGN MESSAGE TODO ERRMESS

Следующий пример дает полезный вариант слова ABORT, который кроме обычно выполняемых операций еще печатает содержимок стека:: .SABORT CR ." В стеке: " .S (ABORT) ;

 ASSIGN ABORT TODO .SABORT

Другими примерами использования векторных слов могут служить переопределение NUM (числовой интерпретатор, используемый в слове INTERPRET) таким образом, чтобы система принимала другие форматы вводимых чисел, например, числа с фиксированной или даже плавающей точкой; изменение слова UPDATE, как указано в разд. 8.3.6, или организация ссылок вперед в рекурсивных определениях (см. ниже).

.цв

3.3. Ссылки вперед

.нф

Нормальный процесс компиляции может компилировать адреса только тех слов, которые были скомпилированы ранее и включены в словарь. Исполнительные векторы позволяют включать в определение ссылки на слова, которые еще на написаны. Чаще всего этого удается избежать перекомпоновкой программы, однако, есть ситуации, когда таких ссылок не избежать.

Примером могут служить два слова, при исполнении которых происходит обращение друг к другу. Простым решением этой проблемы может стать определение "второго" слова как векторного перед написанием определения "первого" слова, например:

 HEX
 EXVEC: PART-TWO
: PART-ONE  CR CR ." Это часть 1" CR ." Вызываю часть 2"
    PART-TWO ;
: PARTWO  CR CR ." Это часть 2" CR
    ." Вызвать часть 1 ? (Y/N) " KEY 89 = IF PART-ONE THEN ;
 ASSIGN PART-TWO T0DO PARTWO
 DECIMAL

.цв

3.4. Рекурсия

.нф

Рекурсивными называются процедуры, использующие обращение к самой себе, как часть определения. Встроеные в компилятор средства защиты не позволяют Форту включать такие обращения в словарные статьи. Поле имени слова в начале его компиляции обрабатывается словом SMUDGE и делается недоступным для поиска текстовым интерпретатором. При успешном завершении компиляции это поле вторично обрабатывается SMUDGE, после чего слово становится известно системе. Это очень эффективный метод для предотвращения исполнения незаконченного или ошибочного определения, но его побочным эффектом является невозможность обращения слова к самому себе.

Популярным решением этой проблемы служит определение слова MYSELF перед написанием рекурсивного определения:

 : MYSELF  LAST PFA CFA , ; IMMEDIATE

Это слово вычисляет адрес поля кода определяемого в данный момент слова и записывает этот адрес по текущему адресу словаря, т.е. в данное же определение. Иными словами MYSELF в обход текстового интерпретатора организует обращение слова к самому себе.

В Форт-Агате потребности в MYSELF нет. Здесь имеется специальная форма определения через двоеточия - слова R: и R; - которая допускает рекурсию:

 : R:  : SMUDGE ;
 : R;  [COMPILE] ; SMUDGE ; IMMEDIATE

Эти слова, ценой снижения защиты Форт-системы, позволяют ссылаться на имя слова, которое в настоящий момент компилируется. Если во время компиляции произойдет ошибка, то незавершенное определение будет включено в словарь со всеми возможными последствиями. Вот пример рекурсии - определение факториала:

 R: (FACT) ( n1 n2 ═→ p3 ) ?DUP IF DUP ROT * SWAP 1-
           (FACT) THEN R;
 : FACT ( n ═→ n! ) DUP 0< OVER 7 > OR 5 ?ERROR
        1 SWAP (FACT) . ;

Если использовать слово MD*, описанное ниже (оно вычисляет произведение двойного числа на одинарное), то можно расширить пределы вычисления факториала до чисел двойной длины:

: MD* ( d1 n ═→ d2 ) 2DUP XOR >R ABS >R DABS R> DUP
      ROT * >R U* R> + R> D+- ;
 R: (FACT)  ?DUP IF DUP 1- >R MD* R> (FACT) THEN R;
 : FACT ( n ═→ ) DUP 0< OVER 12 > OR 5 ?ERROR
     1 0 ROT (FACT) D. ;

.стФортАгат.Имстр2

[an error occurred while processing this directive]