Введение новых слов
Замечательное свойство языка Форт — это возможность вводить в него новые слова, расширяя тем самым набор его команд в нужном программисту направлении. Для введения новых слов чаще всего используется определение через двоеточие — определение нового слова через уже известные. Такое определение начинается словом : (двоеточие) и заканчивается словом ; (точка с запятой). Сразу после двоеточия идет определяемое слово, а за ним — последовательность слов, через которые оно определяется. Например, текст : S2 DUP * SWAP DUP * + ; определяет слово S2, вычисляющее сумму квадратов двух чисел, снимаемых с вершины стека S2 A,B --> A**2+B**2. После ввода данного описания слово S2 можно исполнять и включать в описания других слов. При создании таких определений рекомендуется тщательно комментировать все изменения стека. Слово ( (открывающая круглая скобка) отмечает начало комментария; все следующие литеры до первой ) (закрывающей скобки) считаются комментарием и при обработке вводимого форт-текста пропускаются.
Перепишем приведенное выше определение слова S2, показывая состояние вершины стека после исполнения каждой строки:
: S2 ( A,B ---> A**2+B**2 сумма квадратов) DUP ( A,B,B) * SWAP ( B**2,A) DUP * ( B**2,A**2) + ; ( A**2+B**2)
По-видимому, минимальным требованием к документированности определения следует считать задание начального и конечного состояний вершины стека при работе слова.
Рассмотрим подробнее работу форт-системы во время определения новых слов. Мы уже знаем, что получив от программиста очередную порцию входного текста, форт-система выделяет в ней отдельные слова и ищет их в своем словаре. Эту работу выполняет текстовый интерпретатор форт-системы. Если слово в словаре не найдено, то текстовый интерпретатор пытается понять его как число, используя описанное выше правило умолчания. Если слово найдено или оказалось записью числа, то дальнейшие действия интерпретатора зависят от его текущего состояния. В каждый момент времени текстовый интерпретатор находится в одном из двух состояний: в состоянии исполнения или в состоянии компиляции.
В состоянии исполнения найденное слово исполняется (т.е. выполняется действие, составляющее его семантику), а число кладется на стек. Если же интерпретатор находится в состоянии компиляции, то найденное слово не исполняется, а компилируется, т.е. включается в создаваемую последовательность действий для определяемого в данный момент слова. Найденное и скомпилированное таким образом слово будет исполнено наряду с другими такими словами во время исполнения определенного через них слова. Если требуется скомпилировать число, то текстовый интерпретатор компилирует особый литеральный код, который во время исполнения положит значение данного числа на стек.
Проследим за работой текстового интерпретатора по обработке уже рассмотренного определения слова : S2 DUP * SWAP DUP * + ;. Предположим, что перед началом обработки введенной строки интерпретатор находится в состоянии исполнения. Первым словом является : (двоеточие), которое исполняется. Его семантика состоит в том, что из входной строки выбирается очередное слово и запоминается в качестве определяемого, а интерпретатор переключается в состояние компиляции. Следующие слова, которые интерпретатор будет извлекать из входной строки (DUP, *, SWAP и т.д.), будут компилироваться, а не исполняться, так как интерпретатор находится в состоянии компиляции. В результате с определяемым словом S2 связывается последовательность действий, отвечающая этим словам. Процесс выделения и компиляции слов будет продолжаться до тех пор, пока не встретится ; (точка с запятой). Это слово особенное, оно имеет так называемый «признак немедленного исполнения». Слова с таким признаком исполняются независимо от текущего состояния текстового интерпретатора, поэтому точка с запятой будет вторым исполненным словом после двоеточия. Семантика точки с запятой заключается в том, что построение определения, начатого двоеточием, завершается и интерпретатор вновь переключается в состояние исполнения Поэтому после ввода определения слова S2 мы тут же можем проверить, как оно работает на конкретных значениях:
> 5 4 S2 . 41 ОК
Слова с признаком немедленного исполнения ( другим примером такого слова помимо точки с запятой является открывающая круглая скобка — начало комментария) выполняются по-разному в зависимости от состояния текстового интерпретатора. Некоторые из них даже используются только в одном из этих состояний. Поэтому на диаграмме для таких слов будем отмечать эти случаи, специально указывая состояние компиляции
( ---> ---> (компиляция)
Слово ( в обоих состояниях действует одинаково: пропускает все следующие вводимые литеры до закрывающей круглой скобки включительно или до конца введенной строки, если закрывающая скобка отсутствует.
Слово ; допустимо применять только в состоянии компиляции: ; --> (компиляция). Оно завершает построение нового определения и переключает текстовый интерпретатор в состояние исполнения.
Слово IMMEDIATE (немедленный) --> устанавливает признак немедленного исполнения для последнего определенного слова. (Подробнее использование этого признака рассматривается в п.1.7.)
Введенные слова можно исключить из словаря с помощью слова FORGET (забыть) -->, которое выбирает из входной строки следующее слово и исключает его из словаря вместе со всеми словами, определенными позже.
Разберем следующий протокол диалога:
> 2 2 * . 4 ОК > : 2 3 ; ОК > 2 2 * . 9 ОК > FORGET 2 ОК > 2 2 * . 4 ОК
Сначала программист вычисляет произведение от умножения 2 на 2 и получает ответ 4. Введя затем определение слова 2 как числа 3, он в дальнейшем получает уже другой ответ. Исключив это определение слова 2 через FORGET, он возвращается к прежней семантике слова 2.
В процессе работы текстового интерпретатора программист может переключать его из состояния компиляции в состояние исполнения и обратно с помощью слов [ (открывающая квадратная скобка) --> и ] (закрывающая квадратная скобка) -->. Слово [ имеет признак немедленного исполнения и переключает интерпретатор в состояние исполнения, а слово ] переключает его в состояние компиляции.
Обычно эти слова используются внутри определения через двоеточие, чтобы вызвать исполнение слова или группы слов, не имеющих признака немедленного исполнения. Например, если в тексте определения понадобилась константа FF00 (в шестнадцатиричной системе), а текущей используемой системой является десятичная, то было бы неправильно включить в текст определения фрагмент HEX FF00 DECIMAL, поскольку слово HEX будет не выполнено, а скомпилировано и число не будет воспринято правильно. Вместо этого следует писать: [ HEX ] FF00 [ DECIMAL ]. В языке есть и другие способы, чтобы выразить это же более точно и красиво.
Еще один пример дает использование слова LITERAL (литерал), имеющего признак немедленного исполнения, внутри определения через двоеточие. Оно используется в виде: [ <значение> ] LITERAL, где <значение> — слова, вычисляющие на вершине стека значение, которое словом LITERAL будет скомпилировано в код как число. Во время исполнения определения это значение будет положено на стек. Таким образом, текст [ 2 2 * ] LITERAL внутри определения через двоеточие эквивалентен употреблению слова-числа 4. Это дает большие возможности для использования констант, «вычисляемых» во время компиляции определения. Аналогичное соответствие имеет место и вне определения через двоеточие, поскольку в состоянии исполнения слово LITERAL не выполняет никаких действий, и поэтому на стеке остается вычисленное перед этим значение.