Язык Форт и его реализации

Динамическая идентификация


Внутренним представлением форт-слова является словарная статья, которая размещается в словаре. Через поля связи словарные статьи объединяются в цепные списки, каждый из которых также представлен как отдельный объект словарной статьей, создаваемой по слову VOCABULARY. Исполнение слова, обозначающего такой список, делает его текущим значением переменной CONTEXT, определяющей список, в котором начинается поиск каждого вводимого форт-слова. Если слово отсутствует в этом списке, то следующим просматривается список, в который добавляются создаваемые новые статьи, текущее значение переменной CURRENT. Последним просматривается стандартный список FORTH. Если текстовый интерпретатор находится в состоянии исполнения, то найденный код исполняется, в состоянии компиляции он компилируется в виде ссылки на данную словарную статью для последующего исполнения в составе скомпилированного определения.

Таким образом, в процессе создания нового определения мы индентифицируем составляющие его слова статически, т.е. по текущему контексту (значениям переменных CONTEXT и CURRENT) во время компиляции. Во время исполнения этого определения будут исполняться именно эти, найденные во время компиляции составляющие слова. Вместе с тем в практике программирования уже давно применяется прием, известный как динамическая идентификация. В применении к языку Форт он состоит в том, что вместо компиляции ссылки на статью слова по результату статической индентификации в компилируемой статье запоминается имя слова, и его поиск ведется в момент фактического исполнения. Главная особенность состоит в том, что контекст для поиска также устанавливается динамически в результате предшествующих вычислений.

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

В качестве примера рассмотрим простой язык, аналогичный рассмотренному в , и содержащий описания констант, переменных и оператор присваивания с инфиксной формой записи для выражений. Пусть описания констант выглядят, например, так: КОНСТ А = 2. Чтобы не перегружать наш пример лишними деталями, не будем учитывать возможность компиляции описаний и операторов этого языка. Пусть они сразу же исполняются по мере поступления. Собственно задача состоит в том, чтобы при обработке левой части оператора присваивания можно было различать переменные и константы. В первом случае результатом исполнения должен быть адрес соответствующей ячейки памяти, а во втором — сообщение об ошибке — недопустимом использовании константы в качестве получателя присваивания. Если же имя переменной или константы использовано в правой части присваивания, то в обоих случаях нужно получить соответствующее значение. Разумеется, соответствующие операции проверки можно включить в программу для переменной и константы, анализируя глобальную переменную ?ЗНАЧ, как это сделано в рассмотренном выше примере. Покажем, как эту же задачу можно решить через динамическую идентификацию.

Определим два контекста (списка слов) П и К, содержащих операции над переменными и константами. Таких операций две: ЗНАЧ для получения значения и АДР для получения адреса. Операция ЗНАЧ работает в обоих случаях одинаково, а операция АДР допустима только для переменной; будучи примененной к константе, она должна выдать сообщение об ошибке. В список FORTH также включим слово ЗНАЧ, которое в этом контексте выполняет пустую операцию, и слово АДР, выдающее сообщение об ошибке (их назначение станет ясным чуть позже). Для определения всех этих слов примем, что значение переменной или константы размещается в поле параметров созданной для нее словарной статьи.



FORTH DEFINITIONS VOCABULARY П VOCABULARY К П DEFINITIONS ( ДЕЙСТВИЯ С ПЕРЕМЕННЫМИ) : ЗНАЧ ( PFA->N:ЗНАЧ) @ FORTH ; : АДР ( PFA->A:АДР) FORTH ; К DEFINITIONS ( ДЕЙСТВИЯ С КОНСТАНТАМИ) : ЗНАЧ ( PFA->N:ЗНАЧ) @ FORTH ; : АДР ( PFA->) CR ." НЕДОПУСТИМОЕ ИСПОЛЬЗОВАНИЕ КОНСТАНТЫ " BODY> >NAME ID. ABORT ; FORTH DEFINITIONS : ЗНАЧ ( ->) ; : АДР ( ->) -1 ABORT" НЕДОПУСТИМЫЙ АДРЕС" ;

Заметим, что при исполнении слов ЗНАЧ и АДР в контекстах П и К помимо вычисления соответствующего значения текущий контекст переключается на FORTH. Исполняющая часть определяющих слов ПЕРЕМ и КОНСТ теперь состоит в установке соответствующего контекста, при этом на стек кладется адрес поля параметров данной статьи:

: ПЕРЕМ ( ->) CREATE 0 , DOES> ( PFA->PFA) П ; : КОНСТ ( ->) CREATE BL WORD DROP ( ВЫБРАТЬ ЗНАК =) BL WORD NUMBER DROP ( N:ЗНАЧ) , DOES> ( PFA->PFA) К ;

Чтобы теперь при обработке выражения в левой части присваивания получать значение переменной или константы, надо слегка изменить определение слова >ОПРЦ> (см. ) — включить в него исполнение операции ЗНАЧ, которая идентифицируется динамически по текущему контексту.

: >ОПРЦ> ( N:ПРИОРИТЕТ->) >R " ЗНАЧ" FIND DROP EXECUTE BEGIN ОПРЦ@ R@ < NOT WHILE ОПРЦ> DROP ОПРЦ> ( CFA) EXECUTE REPEAT R> DROP ;

Если это определение исполняется сразу после переменной или константы (в контексте П или К), то оно преобразует адрес поля параметров соответствующей статьи, который находится в этот момент на вершине стека, в значение данной переменной или константы. Для всех других случаев (контекст FORTH) преобразования стека не происходит. Именно по этой причине определения ЗНАЧ в списках П и К по завершении их исполнения переключают текущий контекст на FORTH, а в списке FORTH присутствует определение ЗНАЧ с пустым действием. Последнее обстоятельство гарантирует успешный поиск слова ЗНАЧ в любом контексте, поэтому в приведенном определении проверка того, что поиск закончился успешно, опущена.


В заключение осталось только аналогичным образом переопределить слово := (см. ):

: := " АДР" FIND DROP EXECUTE [COMPILE] ( ;

Сравнивая получившиеся определения, мы видим, что они существенно сократились в объеме: из них исчезли анализ текущего контекста и (соответственно) условные операторы, предписывающие выполнение разных действий в зависимости от результата анализа. Описанный подход особенно перспективен, когда требуется выполнить много разных операций над объектами, каждая из которых выполняется по-разному в зависимости от текущего контекста. Разбиение большой операции с множеством проверок и разветвлений на ряд мелких, каждая из которых выполняется в своем контексте, не только упрощает программирование и отладку, но и сокращает объем программы, так как эти мелкие специализированные операции для разных объектов часто можно совмещать:

К DEFINITIONS : ЗНАЧ ( PFA->N:ЗНАЧ) [ П ] ЗНАЧ ;

В приведенном определении слово ЗНАЧ для списка К не программируется вновь, а определяется через такое же слово из контекста П, тем самым сокращается общий объем отладки. Следуя по этому пути, можно вообще исключить слово АДР из списка К, поскольку в этом случае сообщение об ошибке выдаст слово АДР из списка FORTH, правда, с меньшей информацией — не будет напечатано имя константы. Если механизм динамической идентификации применяется для ряда не связанных между собой целей, то может оказаться неудобным выполнять переключение контекстов через одну и ту же глобальную переменную CONTEXT и выполнять поиск слова в текущем контексте словом FIND. Как правило, все форт-системы имеют более элементарное слово (FIND), которое получает в качестве параметра текстовую строку с именем слова и вход в список статей, в котором нужно выполнять поиск. Используя это слово, можно организовать поиск слова в любом списке или группе списков, уже не связывая его с текущим порядком поиска, принятым для слова FIND.

Например, именно таким образом нетрудно ввести в язык обработку исключительных ситуаций.Для этого нужно завести стек, элементами которого являются списки слов для обработки ситуаций. При установке перехвата ситуаций на вершину этого стека добавляется новый элемент — список слов-обработчиков. При снятии перехвата верхний элемент снимается со стека, делая доступными другие слова-обработчики с теми же именами. Слово, возбуждающее исключительную ситуацию, обращается к (FIND) для просмотра всех списков в этом стеке от верхнего элемента к самому нижнему, сообщая ему имя слова, обозначающего ситуацию. Первое найденное при таком поиске слово и будет использовано как обработчик ситуации. Если слово отсутствует во всех списках, то это ошибка — непредусмотренная ситуация.


Содержание раздела