Язык программирования Форт


Программирование на Форт. Стиль


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

В этой главе подробно рассматривается редактор, представленный в гл. 12. Вместо того чтобы просто анализировать, как работает редактор в целом, мы проведем вас через программу и опишем цепочку решений, которые вынудили нас написать редактор именно так. Вы узнаете больше, если мы скажем вам, как мотивировалось наше решение при написании программы. чем если бы мы только объяснили, каким образом работает редактор. Программирование - в равной мере искусство и наука, и невозможно просто дать правила, которым можно следовать слепо. Каждый программист решит проблему по-разному, и гибкость форта поощряет эту индивидуальность. Итак, раз вы изучаете редактор, попытайтесь подумать о других возможных путях решения проблемы.

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


Задание на программу
Если вам рассказали в деталях, что программа должна делать и как она должна это делать, обычно довольно просто ее написать. В действительности вы не пишите программу, а только транслируете набор инструкций из одной формы (словесное описание) в другую (программа Форт). Хотя процесс трансляции может потребовать сосредоточенности, простое написание программы согласно спецификациям - занятие менее увлекательное и менее творческое, чем выполнение всей работы с самого начала. Если вы отвечаете за задание и за написание программы, вы имеете возможность изменить функцию программы в процессе ее написания. Это то место, где начинается творчество, и Форт этому поможет. Так как вы описали слова, то лучше представляете пути решения ваших проблем и возможности вашей программы.
Программирование является упражнением в решении проблем. Нужно не только понять задачу, но и заставить ЭВМ выполнять процедуру, необходимую для решения задачи. Это включает в себя несколько довольно расплывчатых этапов: описание всей проблемы и цели; разделение проблемы на меньшие задачи, решаемые независимо, и, может быть, последующее деление таких задач; написание программы для этих мелких задач; отладка программы, возможно изменение алгоритма для решения главной проблемы или составляющей ее части и снова отладка; сборка составных частей; окончательная отладка всего пакета. Многие языки маскируют этот естественный процесс: описывается набор алгоритмов, определяющих все, что должна делать программа, а затем пишется программа с минимальными по возможности переделками и отладкой.
Форт допускает другой подход; внутренняя природа решения проблемы при этом более прозрачна. Мелкие проблемы могут решаться одна за другой, проверяться, корректироваться и, может быть, если надо, при этом модифицируются другие части программы как следствие обстоятельств, выявленных в процессе отладки. Это возможно в Форте, так как в нем весьма быстро и легко можно изменить и проверить описание слова или нескольких слов.


Мы использовали эту итеративную модификацию слов несколько раз в предшествующих главах частично как способ показа того, как строить слова на базе основополагающих идей, частично для того, чтобы привести примеры процесса написания Форт-программ. Составление программы на Форте включает в себя написание некоторого числа коротких описаний, использование их в других описаниях и, наконец, сборку всей системы в целом обычно в виде одного слова, которое исполняет программу. Но прежде чем что-либо из этого можно было сделать, вы должны иметь очень точное представление о том, что должна делать программа, т. е. что является главной задачей. Проблема должна быть сформулирована таким образом, чтобы ее можно было поделить на задачи, которые решаются путем описания слов Форта.
Это возвращает нас снова к заданию на программу к описанию того, что должна делать программа. Описание высшего уровня представляет ваши интуитивные цели: что бы вы хотели, чтобы делала программа? Точное описание ваших намерений является наиболее важным шагом в написании программы, так как диапазон ваших целей определяет то, насколько будет полезна ваша программа или даже следует ли ее вообще писать. Этот этап постановки задачи и ошибка здесь будет усиливаться по мере вашей работы и может сделать вашу проблему неразрешимой.
Мы решили написать экранный редактор для этой книги не потому, что было бы полезно для читателей его иметь. Много важнее то, что сам процесс написания редактора является хорошим примером процесса написания любой программы средней сложности. Мы написали редактор для работы по возможности с простейшим терминалом, так как мы хотели сделать его как можно более универсальным, имея в виду, что это сильно упростит его использование читателями с другими типами ЭВМ. Но это ограничение сделало также алгоритм более строгим, а процесс программирования и саму программу более интересными. Таким образом мы не могли полагаться на известные характеристики ЭВМ или терминала, что сделало бы работу легче.




Какова же задача экранного редактора?
Конечно, концепция экранного редактора уже включает многое из того, что должна делать программа. Основной функцией редактора являются вход в режим и модификация текста программы, но существует много способов выполнения этого. Фактическим стандартом для входа в экранный редактор Форта является n EDIT. Эта команда должна отображать содержимое блока n, обеспечивать модификацию содержимого различными путями и последующую запись на диск. Текст изменяется путем перемещения курсора в любом направлении и ввода или стирания символов, на которые указывает курсор. Мы бы также хотели, чтобы редактор позволял нам копировать, замещать, вставлять и стирать строки текста, на которые указывает курсор, а также выполнять некоторые другие операции. Мы можем теперь установить определенное число точно заданных, но достаточно общих спецификаций того, что должен делать редактор.
1. Позволять редактировать блок путем выдачи команды EDIT, отображающей содержимое блока в виде 16 строк по 64 символа и установку курсора в верхний левый угол экрана. 2. Позволять перемещать курсор вправо, влево, вверх и вниз путем нажатия определенных клавиш. Позволять стирание символов нажатием другой клавиши. 3. Позволять замещать символы, на которые указывает курсор, если включен соответствующий режим. 4. Позволять переходить в режим ввода, когда символ не печатается поверх существующего, отмеченного курсором, а вводится так, что остальные символы в строке смещаются, освобождая место для нового. Позволять также переход в режим "замещение" (предположительно переключение из режима в режим производится с помощью командного символа). 5. Позволять стирать строки текста и заносить их в буфер (используя другой управляющий символ) так, чтобы имелась возможность ввести где-то еще. 6. Позволять установить флаг записи на диск (опять же посредством управляющего символа), используя слово UPDATE, чтобы редактируемый блок мог быть записан. 7. Позволять уходить из редактора (посредством еще одного управляющего символа) с последующим уходом в Форт-интерпретатор.


Хотя многие программисты, может быть, и не записывают такие формальные спецификации, они, несмотря на это, держат в уме достаточно точно определенные цели. Мы теперь имеем хорошо описанные цели, но еще не знаем, как разбить программу на слова Форта. Общая задача должна быть сначала разделена на меньшие части. Существует много способов, как это сделать, а для редактора это может быть сделано в уме. Например, мы знаем, что должны разделять текстовые и управляющие символы, выполнять определенные процедуры согласно тому, что требуют управляющие символы, и осуществлять переключение между режимами ввода и замещения. Часто полезно представить проблему на бумаге. Это можно сделать разными способами. Традиционно используется блок-схема. На рис. 13.1 представлена блок-схема редактора. Но блок-схема занимает много места на бумаге и ее неудобно создавать и изменять. Словесное описание функций редактора, как это показано в табл. 13.1, может быть лучше. (Для подготовки таких описаний особенно удобен текстовый редактор, который предоставляет широкие возможности для внесения изменений.)

Так как вы программируете на Форте, то можете предпочесть запись описания в форме псевдофорт, как в табл. 13.2. Слова псевдофорт могут отличаться от тех, что будут использоваться в програме (хотя они могут и предвосхитить имена реальных слов), они имеют то преимущество, что делят всю проблему наилучшим образом.
Таблица 13.1. Словесное описание редактора
Загрузить редактируемый блок в блочный буфер. Отобразить блок, поместить курсор влево вверх, Начать цикл. Получить символ с клавиатуры, Это управляющий символ? Если да, то выполнить команду редактора. Если нет, проверить, является ли он отображаемым, Если да, то проверить - мы в режиме ввода? Если да, то ввести символ в текст. Если нет, то произвести замещение символа. Если нет, то символ игнорируется. Продолжить цикл, пока придет команда выхода из системы
Как мы увидим, существует весьма яркая параллель между табл. 13.2 и словами, которые будут использоваться для выполнения основной работы редактора.


Существует много методов деления программных заданий, начиная от формализма блок-схемы и вплоть до идей, которые вы держите в уме. Вы можете считать полезной комбинацию слов и диаграмм. Важно, чтобы деление на вторичные задачи было выполнено до начала написания программы. Форт требует меньше формализма при разделении проблемы на части, чем многие другие языки, но решение любой задачи (безразлично - программной или нет) выиграет, если она сначала обдумана. Хотя ни одно из этих описаний редактора не является полным, все они задают базовую стратегию решения проблемы. Каждое нажатие клавиши должно обрабатываться индивидуально, для того, чтобы отделить команды от букв, вводимых в текст. Хотя команды редактора не стандартизованы, мы имеем отдельный механизм для их ввода (используя управляющие клавиши). Мы также решили включить два режима (ввод и замещение), позволяющие вводить символы двумя различными способами. Редактор будет работать в бесконечном цикле до тех пор, пока не будет выдана команда EXIT ("выход"), по которой измененный текст будет записан (или нет) на диск. Мы можем теперь рассказать вам, как мы подходили к написанию программы.
Мы начали с уточнения некоторых деталей нашего общего плана. Редактирующие команды, выбранные кодом управляющей клавиши, было бы легко описать, если бы использовался исполнительный вектор. Это делает команды легко задаваемыми, а для пользователя легко изменяемыми по желанию. Простейший путь выполнения выбора ввод-замещение открывает введение флага, который будет устанавливаться одной из команд редактора. Редактор выполняет большинство своих операций в пределах цикла BEGIN... UNTIL отчасти потому, что нужен именно бесконечный цикл (позволяющий выполнять любое число
Таблица 13.2. Описание редактора на псевдофорт
: EDIT ( n -- ) BLOCK-TO-BUFFER BLOCK-TO-SCREEN CURSOR-TO-START BEGIN FETCH-CHARACTER CONTROL-CHAR? IF DO-EDIT ELSE PRINTABLE-CHAR? IF INSERT-MODE? IF INSERT ELSE OVERTYPE THEN ELSE DROP THEN THEN END-FLAG UNTIL ;


редактирующих операций), отчасти из- за того, что EXIT только меняет флаг, лежащий в стеке, для того чтобы выйти из цикла при UNTIL. В нашей концепции необходимо еще принять решение о многих других редактирующих командах и об использовании памяти ЭВМ.
Мы имеем выбор позволить редактору непосредственно модифицировать содержимое блочного буфера или перенести его содержимое в другую часть памяти (PAD и далее) и редактировать его там. Был выбран последний метод, так как он позволяет "отменять" изменения и таким образом обеспечивать некоторую защиту против изменений на диске, внесенных случайно. Карта распределения памяти показана в табл. 13.3. Слово BLOCK используется для загрузки блока, номер
Таблица 13.3. Предварительные соображения о распределении памяти для редактора
Блочный Переносится PAD Отображается Терминал буфер в на
Первая строка --> Первая строка --> Первая строка Вторая строка --> Вторая строка --> Вторая строка ............. --> ............. --> ........... Последняя строка --> Последняя строка --> Последняя строка
которого хранится, в стеке, в блочный буфер, откуда 1024 символа переносятся командой CMOVE в PAD, где будет проводиться редактирование. Когда нужно сохранить изменения, 1024 символа из PAD переносятся в блочный буфер, который помечается для сохранения оператором UPDATE. Блок из 1024 символов отображается на экране терминала. Положение курсора на терминале и положение соответствующего символа в PAD оказываются связанными так, что изменение положения курсора означает изменение указания на символ в тексте, лежащем в массиве выше адреса PAD. Положение курсора логически характеризуется кодами строки и столбца, номера которых отсчитываются от левого верхнего угла дисплея. Эти коды могут быть затем использованы для получения любой другой информации, связанной с положением курсора.
Одной из особенностей редактора, в которой мы нуждались, была возможность вводить и стирать строки текста, на которые указывает курсор.


Память сразу за последней отображенной строкой (начиная с PAD+1024) может использоваться для хранения бесконечного числа стертых строк, которые могут быть вставлены в отображаемый текст, если это желательно.
Таблица 13.4. Использование памяти редактора
Блочный Перено- PAD Отобража- Терминал буфер сится в ется на
Первая строка --> Первая строка --> Первая строка Вторая строка --> Вторая строка --> Вторая строка .......... --> ............. --> ............. Последняя строка --> Последняя строка --> Последняя строка 1-я скрытая строка 2-я скрытая строка 16-я скрытая строка Дополнительная строка Строка для операций копирования-замещения
Представляется разумным включить 16 строк в скрытый буфер так, чтобы содержимое всего блока могло быть стерто и затем восстановлено где-то еще. Если процедуры ввода и стирания строки были спроектированы для работы в кольцевом режиме, ни одна строка не будет потеряна. Так как отображаемый и скрытый буфера смежны, программа для реализации этого кольцевого буфера будет весьма проста. Окончательное распределение памяти в редакторе, выбранное нами, включая дополнительную строку для процедур копирования-замещения, показано в табл. 13.4.
Представляется полезным добавить кольцевой буфер к редактору, и это можно рассматривать в качестве примера для модификации других редакторов, не имеющих таких возможностей. (Как это работает, будет показано в деталях позднее.)
Этого обзора было бы достаточно, чтобы начать писать редактор, но мы добавили еще два ограничения. Мы знали, что надо свести число терминальных операций к минимуму, чтобы редактор работал с наиболее широким спектром ЭВМ. После некоторого размышления мы решили спроектировать редактор, который работает, используя только две терминальные команды: 1 - очистка экрана и 2 - установка курсора на заданную строку и столбец. Было ли это практичным, стало ясным после того, как редактор был частично написан, но это была цель, за которую мы боролись. Позднее мы ограничили нашу задачу написанием редактора для минимального размера экрана 16 строк по 64 столбца, что упростило текст программы.


Это завершило спецификацию " высокого уровня" для редактора; теперь мы в основном поняли, как должен работать редактор. Продолжать более общее описание функций редактора без реального решения некоторых проблем "нижнего" уровня было бы в заметной мере пустой тратой времени. Например, вдруг число слов управляющих терминалом, нельзя сократить до 2? Теперь мы были готовы начинать писать Форт-программу. Для большинства языков программирования требуется много, больше планирования, так как модульная структура, присущая словам Форта, им недоступна. Если вы изучаете Фортран, Кобол или большинство других языков, в ваших интересах спланировать все как можно подробнее, прежде чем написать строчку программы. Например, вам надо выбрать, какой управляющей клавише присвоить какую функцию, и вы должны мысленно спроектировать все управляющие функции терминала. Процесс разработки при этом будет дольше и конечный продукт, вероятно, не будет столь уж хорош. Форт позволяет писать и отлаживать программы методом проб и ошибок или на интерактивной основе. Это выявляет многие ошибки прежде, чем они получат шанс распространиться непредсказуемым путем через программу. Мы не будем описывать все пробы и ошибки, которые были совершены при написании редактора, но попытаемся представить вам идеи некоторых шагов, которые предпринимались.
Закладка фундамента
До сих пор мы строили замок грез, лишенный фундамента. Наши размышления привели к ясному пониманию того, какой редактор мы хотим создать, и к конкретным идеям построения некоторых его структур высокого уровня. Но прежде чем мы сможем проверить какие-либо наши идеи, мы должны определить слова, на которых будет базироваться все остальное. Должны быть описаны переменные и константы (хотя некоторые из них могут быть добавлены позднее). Должны быть зафиксированы некоторые адреса памяти. Нужно описать слово или слова, управляющие функциями терминала. Должна быть решена проблема, как изменить и запомнить положение курсора на экране терминала и соответствующие позиции в буфере редактора.


Эти и другие аспекты работы редактора должны быть рассмотрены на ранних этапах.
Хотя мы опишем окончательный текст программы блок за блоком (чтобы сделать ее более понятной), сам редактор пишется не так. Мы сначала написали редактор, который может делать очень немногое, чтобы кое-что проверить. Базовые слова дописывались позднее в процессе написания программы и были помещены в первые два блока, чтобы сохранить иерархию функций слов от наименее к наиболее сложным и сделать программу простой для понимания. Мы группировали слова по их функциям настолько, насколько возможно, в некоторых случаях проводя переукладку после того, как программа была функционально завершена.
Кто-то может сказать, что текст программы написан слишком плотно, но мы должны были экономить место в книге. Нужно оставить достаточно свободного места в программе, чтобы можно было выполнить переукладку слов согласно их функциям, а также чтобы осталось место для изменений и добавлений. Если вы делаете много изменений в тексте программы, вам нужно место для ее расширения. Первые два блока редактора содержат описания констант, переменных, флагов и адресов некоторых буферов и базовые слова для управления курсором и терминалом. Переменные ROW и COL (строка и столбец, где находится курсор) и I/R (флаг ввод-замещение) были действительно описаны первыми. Остальные были добавлены по мере необходимости. Позднее в процессе программирования с целью экономии памяти были описаны некоторые числа (BL, 1K, 64 и т.д.). PDELAY и SDELAY представляют собой два цикла, организующие задержки для отображения информационных сообщений.
0 ( 20 июля 85 Экранный редактор NS 01 из 10) 1: TASK ; DECIMAL 2 3 32 CONSTANT BL 1024 CONSTANT 1K 4 64 CONSTANT 64 63 CONSTANT 63 5 10000 CONSTANT PDELAY 5000 CONSTANT SDELAY 6 VARIABLE SCR VARIABLE ROW 7 VARIABLE COL VARIABLE I/R 8 VARIABLE LOWBLK VARIABLE HIGHBLK 9 ( запоминание первого и последнего блоков, которые можно редактировать на вашей ЭВМ) 10 1 LOWBLK ! 169 HIGHBLK !
Они являются константами, чтобы можно было изменить задержки, не изменяя текста программы.


Константы и переменные должны быть собраны в начале программы (или в начале секций большой программы) для того, чтобы их было легко найти и изменить. Переменная SCR была описана для тех версий Форта, где этого слова нет. Переменные LOWBLK и HIGHBLK были добавлены для того, чтобы редактор не имел доступа к тем блокам, к которым не следует. Они могут быть изменены в тексте программы или с клавиатуры после того, как редактор загружен. 11 : --- ; ( слово, не выполняющее никакой работы для исполнительного вектора) Это слово (названное так, чтобы бросаться в глаза в тексте программы) позволяет использовать неописанные управляющие клавиши в исполнительном векторе KEYVECTORS (блок 9). Это слово также было описано, когда был описан KEYVECTORS, но помещено здесь, так как это слово низкого уровня. 12 : MODE (-) I/R @ 0= I/R ! ; ( выбор ввод-замещение) Каждый раз, когда используется MODE, оно переводит значение I/R из состояния истинно в состояние ложно или наоборот. MODE действует как переключатель, который выбирает режим ввода символов. 13 : DELAY ( n - ) 0 DO LOOP ; ( Задержка на n циклов) 14 : PAUSE ( - ) PDELAY DELAYS ; ( Длинная задержка) 15 : SPAUSE ( - ) SDELAY DELAYS ; ( Короткая задержка) Эти циклы задержки были описаны позднее, но перемещены в начало текста программы. PAUSE и SPAUSE были описаны как отдельные слова, чтобы в дальнейшем сэкономить место в программе. Основополагающие слова размещены в следующем блоке. 0 ( 20 июля 85 Экранный редактор NS 02 из 10) 1 : PADDR ( n - ) CREATE , DOES> @ PAD + ; 2 : 1K 2 * PADDR BLINE ( Последняя строка кольцевого буфера) 3 : 1K 3 * 64 + PADDR LBUFF ( Адрес строки для копирования-эамещения)
Слово-описатель PADDR ("PAD ADDRESS" = адрес PAD) используется для формирования двух адресов, необходимых при работе с буферами редактора. Для симметрии мы могли бы описать также О ADDR OLINE, чтобы получить адрес PAD (начало буфера редактора), но в тексте программы использовался PAD для того, чтобы подчеркнуть, какой именно адрес памяти используется.


Команды 1K 2 * и 1K 2 * 64 + применены для вычисления значений 2048 и 2112, так как это легче понять в терминах блоков и строк, которые, в свою очередь, дают лучшие представление о структуре буфера редактора. Заметьте, что мы не заботимся о времени вычислений, так как они выполняются только раз, во время компиляции. 4 : CONTROL ( с --) CREATE DEPTH DUP С, 0 DO DEPTH ROLL С, 5 LOOP DOES> DUP DUP C@ + SWAP DO I 1+ C@ EMIT LOOP ; 6 27 42 CONTROL ( Очистка экрана, курсор вверх влево)
CONTROL - слово-описатель, сформированное для описания слов, которые генерируют управляющие коды и ESC-последовательности. На терминале ADM 31 работает 27 42 CONTROL
так как ESC-последовательность ESC * (ASCII 27 и последующий ASCII 42) используется для очистки экрана. Хотя CONTROL имеет встроенное описание, это слово упрощает описание слов управления терминалом и делает текст программы более читаемым. Описание : 27 EMIT 42 EMIT ; также будет работать, но применение слова-описателя CONTROL при наличии нескольких описаний сэкономит память. Слова, имеющие отношение к функциям терминала, снабжены именами, заключенными в треугольные скобки, чтобы они выделялись в тексте программы. 7 : 27 EMIT 61 EMIT ROW @ 32 + EMIT COL @ 32 + EMIT ; 8 ( Положение курсора в строке Х и столбце Y) Это описание демонстрирует типовой образец слова, осуществляющего прямое позиционирование курсора. Оно работает с ADM 31 и, конечно, будет переделано для других терминалов или ЭВМ. 9 ( : PAGE ; : ROW @ COL @ PTC ; ) Это показывает, как можно описать и , применяя слова Форта, а не ESС-последовательности (эти примеры используют слова MMSFORTH). 10 ( Чтобы использовать далее . , , и ) 11 ( необходимо описать их здесь. Используется лишь в 12 случае отсутствия прямого перемещения курсора) 13 ( 28 CONTROL (перемещение курсора вверх влево) 14 ( : ROW @ ?DUP IF 0 DO LOOP THEN ) 15 ( COL @ ?DUP IF 0 DO LOOP THEN ; )
Требуется только одно описание ("cursor-X-Y") и ("clear"); это альтернативное описание медленнее и основано на


("downward-line-feed" = переход на одну строку вниз) и ("advance" = сдвиг курсора на одну позицию), которые описываются оператором CONTROL. Это слово следует использовать, только если терминал позволяет очень примитивное управление курсором. Чтобы сделать программу легко читаемой, все слова, описанные CONTROL, нужно сгруппировать вслед за описанием самого слова CONTROL.
В блоке 3 продолжается описание слов, управляющих курсором. 0 ( 20 июля 85 Экранный редактор NS 03 из 10) 1 : @CURSOR (- r c ) ROW @ COL @ ; ( Вызов строки и столбца) 2 ; !CURSOR ( r c -) COL ! ROW ! ; ( Запоминание строки и столбца)
@CURSOR и 1CORSOR были описаны, чтобы упростить вычисления и запоминание положения курсора. Это было сделано после того, как мы выяснили, что комбинации, которые они представляют, появляются в нескольких местах текста разрабатываемой программы. Эти слова довольно часто встречаются попарно в одном и том же описании и делают программу легко читаемой. Представление положения курсора через номера колонок и столбцов (а не только через номера байта) упрощает перемещение курсора и описание некоторых последующих слов, 3 ( опишите здесь, если возможно, и и используйте в блоках 3 и 4) 4 : 0 COL ! ; ( Перемещение курсора в начало строки) 5 : 0 0 !CURSOR ; ( Перемещение курсора вверх влево) Слова ("start-line" - начало строки) и ("home-cursor" - курсор на место) могут быть описаны с помощью CONTROL (если ваш терминал поддерживает эти функции) и использованы вместо в этих определениях. Заметьте, что положение курсора должно отслеживаться в двух местах: его реальное положение запоминается в ROW и COL, но курсор должен быть также помещен в соответствующую точку на экране. 6 : BABOVE ( - n ) ROW @ 64 * ( Число байт в рядах выше курсора) 7 : BBELOW ( - n ) 16 ROW @ - 64 * 1К + ; ( Число байт ниже курсора) 8 : OFFSET ( - n ) BABOVE COL @ + ; ( Число байт в буфере перед курсором) 9 Неудачно названные BABOVE ("bytes-above" - число байтов до) и BBELOW ("bytes-below" - число байтов после) - слова, которые пригодятся позднее.


Они вычисляют число байтов (в буфере) в строках выше курсора и число байтов (также в буфере) в строке, где находится курсор, а также в строках ниже его (включая весь кольцевой буфер). Слово OFFSET кладет в стек число байтов, отсчитанное от начального положения курсора (верхний левый угол). Так как это слово используется только в CPOS, OFFSET является излишним, оно лишь делает описание CPOS более легким для понимания. Обратите внимание, что любое изменение величины ROW или COL поменяет число, выдаваемое в стек оператором OFFSET (и CPOS). 10 : CPOS ( - адр) PAD OFFSET +: ( адрес курсора) 11 : LSTART ( - адр) PAD BABOVE +: ( адрес начала строки) 12 : LEND ( - адр) LSTART 63 +: ( адрес конца строки) Слова CPOS ("cursor-position" - положение курсора), LSTART ("linestart - начало строки) и LEND ("line-end" - конец строки) выдают три адреса в буфере, которые широко используются в последующем тексте редактора. 14 : BLEFT ( -n ) LEND CPOS - ; ( Число байтов в строке слева от курсора) 15 BLEFT ("bytes-left" - байтов осталось) необходимо для последующих слов, которым нужна информация о числе байтов (символов) между курсором и концом строки.
Заметьте, что положение курсора в буфере задается несколькими способами. Мы знаем его строку и столбец, число байтов до и после него в PAD-буфере, число байтов от начала буфера, его адрес в памяти, адрес начала и конца строки, где он находится, и число байтов, лежащих в строке после курсора. Мы теперь имеем полный набор слов, позволяющий нам описать большое число слов высокого уровня в блоке 4 0 ( 20 июля 85 Экранный редактор NS 04 из 10) 1 ( Если возможно, опишите , , и здесь для того, чтобы использовать их) 2 ( вместо в описаниях на) 3 ( строках 4, 5, 6 и 7. ) 4 : LEFT COL @ 0 > IF -1 COL +! (или ) THEN : 5 : RIGHT COL @ 63 < IF 1 COL +! (или ) THEN ; 6 : UP ROW @ 0 > IF -1 ROW +! (или ) THEN ; 7 : DOWN ROW @ 15 < IF 1 ROW +! (или ) THEN ;
Первая и наиболее важная вещь, которую мы хотели бы сделать, это двигать курсор в четырех направлениях, но не покидая экрана.


Слова и т.д. должны быть описаны с помощью CONTROL, если ваш терминал или ЭВМ позволяют это, и затем использованы вместо в этих словах. Заметьте, что ROW и COL должны так изменяться, чтобы отслеживать соответствие положения курсора и адреса в буфере редактора. 8 : ( --) BLEFT SPACES ; ( Стереть конец строки) Было бы лучше описать оператор ("erase-end-line" - стереть конец строки) с помощью оператора CONTROL при условии, что ваш терминал позволяет это. 9 : NEWLINE ( -- ) DOWN ; ( Курсор в начало следующей строки)
Имени NEWLINE будет поставлена в соответствие клавиша (которая формирует код, эквивалентный CTRL-M), чтобы установить курсор в начало следующей строки. Это слово имитирует функцию "возврат каретки + перевод строки" (). Однако в редакторе ВК только перемещает курсор; это не должно вызывать перемещение текста на экране. 10 : SHOWLINES ( - ) @CURSOR 16 ROW @ ( отображение строк) 11 DO I ROW ! LSTART 64 -TRAILING TYPE LOOP 12 !CURSOR ; 13 : SHOWBLK ( -- ) @CURSOR SHOWLINES ICURSOR ;
Оператор SHOWLINES отображает строку, где находится курсор и все последующие строки до конца блока. Когда изменена только нижняя часть экрана, это экономит время по сравнению с отображением заново всего экрана. Оператор SHOWBLK использует оператор SHOWLINES для отображения всего блока после перемещения курсора влево вверх. Обратите внимание на то, как @CURSOR и ICURSOR используются в паре для занесения в стек кодов исходного положения курсора и для последующего в конце работы слова восстановления его позиции. 14 : TYPELINE ( -- ) CPOS BLEFT 1+ OVER OVER TYPE -TRAILING 15 SWAP DROP BLEFT 1+ -SPACES ;
Оператор TYPELINE пропечатывает все символы, начиная* от курсора и до конца строки. -TRAILING используется для определения числа пробелов, необходимых для того, чтобы избавиться от лишних символов, которые в противном случае появятся за пределами 64- символьной строки.
Мы имеем теперь к концу четвертого блока описания почти всех слов низкого уровня, в частности слов, оперирующих адресами памяти, перемещающих курсор и отображающих текст на экране.


Только некоторые из действительно редактирующих команд были описаны в первых четырех блоках, но в следующих четырех блоках мы опишем остальные.
Упражнения
1. Почему строка 1 блока 1 записана следующим образом : TASK ; DECIMAL 2. Почему лучше описать LOWBLK и HIGHBLK как переменные, а не как константы ? 3. Почему PDELAY и SDELAY описаны как константы, а не использованы непосредственно числа в описаниях PAUSE и SPAUSE? 4. Приведите две причины для выделения DELAYS из описаний PAUSE и SPAYSE. 5. Предположим, что вы используете терминал ADM 31 и хотите добавить слова PAGE и РТС из MMSFORTH в ваш Форт. Сформулируйте их описание. 6. Предположим, что вы работаете с терминалом, на котором работают ESC-последовательности: 27 13 Сместить курсор на одну строку вниз, 27 14 Сместить курсор вперед на один символ, 27 15 Сместить курсор вверх влево (HOME) Опишите , и в строках 10, 11 и 12 блока 2. Что бы бы теперь сделали, чтобы использовать описание , приведенное в строках 14 и 15 ? 7. Почему модификации, предложенные в строках 1, 2 и 3 блока 4, были бы предпочтительнее, если они возможны?
Основные положения
Теперь, когда фундамент заложен, в следующих четырех блоках может быть описано большинство редактирующих команд. Эти операторы основаны большей частью на словах, которые мы уже описали, а не на словах, описанных в Форте. Наибольшее удовольствие доставляет написание программ Форта среднего и наивысшего уровней, так как вы при этом используете в основном язык, созданный вами самими. Но на этой фазе проявляются противоречия и пропуски, допущенные в написанных ранее программах. Это как раз то время, когда вы оцените оставленное вами свободное место в предшествующих блоках для расширения возможностей и внесения изменений. Пока мы писали редактор, блоки 1-4 занимали на самом деле 7 блоков. Порядок, в котором описываются слова промежуточного уровня, до некоторой степени произволен, поскольку большинство из них являются независимыми. Разумно помещать связанные слова в одном и том же блоке хотя бы для того, чтобы легче было их найти и отредактировать.


0 ( 20 июля 85 Экранный редактор NS 05 из 10) 1 2 Ж BCLEAR ( -- ) PAD 1K + 1152 BL FILL : ( Очистка буфера) 3 : SCLEAR ( -- ) PAD 1K BL FILL SHOWBLK ; ( Очистка экрана) 4 Имеются слова для очистки содержимого кольцевого буфера и дисплея. 5 6 : LOADBLK ( - ) SCR @ BLOCK PAD 1K CMOVE ; ( Загрузка блока) 7 : RESTORE ( - ) LOADBLK SHOWBLK ; 8 Слово LOADBLK ("load-block" - загрузить блок) производит загрузку в блочный буфер, а затем и в буфер редактора. RESTORE загружает буфер редактора и отображает его содержимое. RESTORE аннулирует редактирование, проведенное с момента, когда блок был последний раз отредактирован и сохранен. Функция RESTORE оказалась полезной сама по себе при написании программ (она отсутствовала в исходной спецификации) и оказалась практичной, так как блочный буфер не используется непосредственно при редактировании. 9 : (UPDATE) ( - ) PAD SCR @ BLOCK 1K CMOVE UPDATE ; ( Пометка для сохранения)
Это слово копирует отображаемую часть буфера редактора в блочный буфер и помечает для сохранения. Его имя в скобках предполагает, что оно тесно связано с функцией UPDATE, описанной ниже. 10 11 : +BLK ( - ) SCR @ HIGHBLK @ < IF 1 SCR +! RESTORE THEN ; 12 : -BLK ( - ) SCR @ LOWBLK @ > IF -1 SCR +! RESTORE THEN ;
Эти слова используются для начала редактирования следующего или предшествовавшего блока. Заметьте, что блок, который вы покидаете, должен быть помечен для сохранения (UPDATE), если изменения, сделанные в нем, нужно записать на диске, прежде чем вы продолжите работу. HIGHBLK и LOWBLK отсутствовали в исходной спецификации; они были добавлены позднее для того, чтобы предотвратить доступ редактора к запретным или несуществующим блокам. 13 14 : CLRTOP ( - r c ) @CURSOR ; 15 : WRITETOP ( r с -) TYPELINE !CURSOR ;
Слова CLRTOP ("clear-top" - очистить верх) и WRITETOP были описаны для упрощения написания подсказок, которые используются в четырех словах следующего блока. При совместном использовании они запоминают положение курсора в стеке, заполняют верхнюю строку на терминале пробелами, восстанавливают верхнюю строку и устанавливают курсор туда, где он был.


Четыре слова в блоке 6 выдают информацию оператору и (или) обеспечивают выбор из меню. 0 ( 20 июля 85 Экранный редактор NS 06 из 10 ) 1 : ?BLKS ( -- ) CLRTOP ." * * * BLOCK#: " SCR @ . ( блок# ) 2 PAUSE WRITETOP ;
?BLK# ("block-number" - номер блока) отображает текущий номер блока на верхней строке, ждет некоторое время, а затем восстанавливает верхнюю строку на дисплее. Это слово просто напоминает оператору номер блока, который редактируется. Длительность паузы задается константой PDELAY. 4 : UPDATES ( -- ) (UPDATE) ( пометить блок и показать это) 5 CLRTOP ." * * * UPDATED Block#: " SCR @ . SPAUSE WRITETOP ;
Оператор UPDATES позволяет вам пометить блок для сохранения и затем подтвердить эту операцию, используя более короткую паузу, чем в ?BLK#. Отображение для этого необязательно, но без него у вас не будет средств убедиться, что пометка (UPDATE) действительно произошла. 6 7 : ?CLEAR ( - ) CLRTOP ." * * * X-OUT; (B)uffer, (S)creen ?" 8 KEY DUP 66 = IF DROP BCLEAR 9 ELSE 83 = IF SCLEAR !CURSOR EXIT THEN 10 THEN WRITETOP;
Это и следующее слова представляют субменю, которое позволяет присвоить заданные функции определенным управляющим клавишам в основной программе. В 7CLEAR ("очистить?") предполагается три варианта: очистить буфер, экран или ничего (ничего, если вы нажмете любую клавишу, кроме "В" или "S"). Верхняя строка восстанавливается, если информация на экране не была стерта (что показывает, насколько полезной может быть операция EXIT). В любом случае курсор будет установлен туда, где он был ранее. 11 : ?EXIT ( f - ) CLRTOP ." * * * EXIT; (S)ave, (Q)uit?" 12 KEY DUP 83 = 13 IF DROP DROP DROP UPDATE FLUSH 1+ EXIT 14 ELSE 81 = IF DROP DROP EMPTY-BUFFERS 1+ EXIT THEN 15 THEN WRITETOP ;
Это слово, используемое для ухода из редактора, работает во многом так же, как и ?CLEAR, представляя три варианта, прежде чем делать что-либо. Слово 1+ становится понятным, когда вы заглянете в EDITLOOP в последнем блоке, оно устанавливает флаг, позволяющий выйти из редактора.


EXIT используется здесь так же, как и ?CLEAR, для предотвращения в WRITETOP восстановления верхней строки при выходе из редактора.
Следующий блок содержит слова, необходимые для ввода текста и изменения его различными путями. Существуют два основных способа или режима, используемые для ввода символов: замещение символа, на который указывает курсор, или раздвижка текста (перемещение остальной части строки на одну позицию вправо) перед вводом нового символа. 0 ( 20 июля 85 Экранный редактор NS 07 из 10) 1 : OPENUP ( - ) ( Раздвинуть текст в месте буфера, куда указывает курсор) 2 COL @ 64 < IF CLOS DUP 3 1+ BLEFT ) BL CPOS C! THEN ; 4 : OPEN ( -- ) OPENUP TYPELINE ; ( Раздвинуть текст в месте, указанном курсором)
OPENUP необходимо для ввода пробела в позицию, указанную курсором, перед вводом символа. Это слово воздействует только на буфер, ничего не отображая на экране. Если вы не хотите терять конец строки, который выдвигается в правую сторону дисплея, проверяйте последний символ в строке (используя LEND @), чтобы убедиться, что это пробел, прежде чем позволить слову OPENUP делать чтобы-то ни было. Слово OPEN позволяет вам вводить пробел в позицию, отмеченную курсором, смещая текст на один символ вправо. Это побочный продукт слова OPENUP, которое все равно нужно было написать для INSERT. 6 : TRUNC ( -) CPOS BLEFT BL FILL ; ( Укоротить строку)
TRUNC ("отбросить") заполняет пробелами часть строки, лежащую между курсором и концом строки. 7 8 : OVERTYPE ( Симв - ) COL @ 64 < ( Заместить символ, на который указывает курсор) 9 IF DUP EMIT CPOS С! 1 COL +! ELSE DROP THEN ; 10
Это основное слово для замещения символа, на который указывает курсор, символом, лежащим в стеке. Если курсор не находится за краем строки, символ, лежащий в стеке, отображается на терминале и записывается в соответствующем месте буфера редактора. Положение курсора в буфере (COL) увеличивается на 1, чтобы обеспечить соответствие с положением терминального курсора, который при отображении символа перемещается на одну позицию вправо. 11 : INSERT ( симв - ) OPENUP OVERTYPE TYPELINE ; ( Ввести символ )


Слово INSERT сначала раздвигает текст, затем замещает пробел, возникший на месте курсора, символом, лежащим в стеке, после чего отображает строку. 12 : DELETE ( - ) COL @ 64 < ( Стереть символ) 13 IF CPOS 1+ CPOS BLEFT CMOVE BL LEND C! 14 TYPELINE 15 THEN ;
Стирание символа выполняется путем перемещений текста между курсором и концом строки (в буфере редактора) на одну позицию влево, записи пробела в конец строки (в буфере), стирания строки на экране и отображения измененной строки.
Следующий блок работает с невидимыми на экране буферами (строчный и кольцевой буферы). Строчный буфер используется для получения копии строки, которая заменит строку где-либо еще. Он особенно полезен для запоминания строки заголовка, которая будет использоваться в нескольких блоках. Кольцевой буфер воспринимает одну или более строк, которые были стерты. Эти строки затем могут быть вставлены где-либо еще в том же или другом блоке. Если вы хотите избавиться от некоторых строк совсем, то укоротите их с помощью оператора TRUNC при положении курсора на левом поле, прежде чем стирать и помещать их в кольцевой буфер. Слово BCLEAR очистит все строки в кольцевом буфере (см. его описание в блоке 5). То, что вы видите на экране, если блок помечен (UPDATE), то и будет записано на диск. 0 ( 20 июля Экранный редактор NS 08 из 10) 1 : CLINE ( --- ) LSTART LBUFF 64 CMOVE ; ( Копирование строки в буфер) 2 3 : PLINE ( - ) LBUFF LSTART 64 CMOVE ( Извлечь строку из буфера ) 4 CURSOR TYPELINE !CURSOR ;
CLINE ("copy-line" - скопировать строку) и PLINE ("put-line" - вставить строку) работают с буфером строки (по адресу LBUFF), предназначенным для копирования и замещения отдельной строки. Полезной модификацией редактора было бы постоянное отображение содержимого строчного буфера сразу под текстом блока на экране, если ваш экран имеет достаточно места. 5 6 : KLINE ( -- ) ( Стереть строку, занеся ее в кольцевой буфер) 7 LSTART BLINE 64 CMOVE ( Перенос текущей строки) 8 LSTART 64 + LSTART BBELOW CMOVE ( Сдвинуть буфер вверх) 9 SHOWLINES ; ( Отображение измененных строк)


Слово KLINE ("kill-line" - стереть строку) стирает строку, на которую указывает курсор, из отображаемой части буфера редактора и переносит ее в кольцевой буфер по адресу BLINE. Затем весь буфер редактора, начиная со строки ниже курсора и кончая BLINE, сдвигается на одну строку (64 символа), так что перекрывает стертую строку. Слово SHOWLINES отображает строки, начиная с помеченной курсором до конца экрана. Последней строкой экрана станет верхняя строка кольцевого буфера, в котором строки при работе перемещаются по ротационной схеме. 11 : ILINE ( --) ( Ввести строку из кольцевого буфера) 12 LSTART DUP 64 + BBELOW ) ( сдвинуть буфер) 13 BLINE LSTART 64 CMOVE ( Перенести строку с низа буфера) 14 SHOWLINES ; ( Отображение измененных строк)
Слово ILINE ("insert-line" - ввести строку) сдвигает кольцевой буфер на одну позицию. Весь буфер редактора, начиная со строки с курсором и кончая строкой перед BLINE. смещаются вниз на 64 символа, а последняя строка (теперь по адресу BLINE) переносится, чтобы заместить строку, где был раньше курсор; изменения отображаются оператором SHOWLINES. Это выглядит как вращение кольцевого буфера вниз на одну строку. Здесь описаны все функции, используемые редактором. Остается только соединить эти функции вместе, чтобы редактор выполнял то, что было вначале задумано.
Соединение частей в единое целое
Наиболее интересная фаза написания любой программы - это внесение последних поправок и наблюдение за тем, как она, наконец, работает. В Форте последняя фаза доставляет даже большое удовольствие, так как вы проверяете слова промежуточного уровня и теперь видите, как они собраны и как работают совместно. Если вы хорошо поставили задачу и выразительно назвали ваши слова, сборка всего вместе - обычно наиболее простая часть написания программы на Форте.
Простейший способ доступа ко всем 23 командам редактора - это создать исполнительный вектор для командных слов. Если используется клавиша CTRL в комбинации с буквой от "А" до "Z", то это дает 26 возможных команд.


Слово -- было создано в блоке 1, чтобы поставить в соответствие неиспользуемым управляющим символам пустое слово, не выполняющее никакой работы.
0 ( 20 июля 85 Экранный редактор NS 09 ИЗ 10) 1 CREATE KEYVECTORS ] ( Исполнительный вектор команд редактора) 2 LEFT ( А Курсор влево ) ?BLK# ( В Номер блока ) 3 GLINE ( С Копирует строку ) DELETE ( D Стирает символ ) 4 ?EXIT ( Е Уход из редактора ) --- ( F ) 5 MODE ( G Переход в новый режим ) --- ( Н ) 6 ILINE ( I Ввод строки ) --- ( J ) 7 KLINE ( К Стирание строки ) -BLK ( L Последний блок ) 8 NEWLINE ( М или ENTER, CR+LF ) +BLK ( N Следующий блок ) 9 OPEN ( O Раздвинуть текст ) PLINE ( Р Вставить строку ) 10 ( Q Курсор на место ) RESTORE ( R Восстановить экран ) 11 RIGHT ( S Курсор вправо ) TRUNC ( Т Укоротить строку ) 12 UPDATES ( U Поместить буфер ) --- ( V ) 13 UP ( W Курсор вверх ) ?CLEAR ( X Очистить буфер/экран ) 14 --- ( Y ) DOWN ( Z Курсор вниз ) [
Дополнительные приложения слов ] и [ описаны в гл.15, но здесь достаточно знать, что они помещают адреса слов, записанных между ними, в тело описания KEYVECTORS, как это требуется для исполнительных векторов. Функции, присваиваемые клавишам, произвольны, и мы действительно меняли местами команды в процессе написания программы; вы можете делать то же самое. Оператор KEYVECTORS может быть написан так, чтобы занимать намного меньше места на экране. Но тогда для документирования функций клавиш потребовалась бы отдельная таблица команд; более эффективно было бы позволить описанию слова документировать само себя. 15 : KEYDO ( n -) 1- 2* KEYVECTORS + @ EXECUTE ;
Слово KEYDO воспринимает число (от 1 до 26, от CTRL-A до CTRL-Z) и исполняет n-ю команду в KEYVECTORS. Теперь любая редактирующая команда может быть использована нажатием одной клавиши с пульта. Блок 10 как раз то место, где все соединяется в единое целое. 0 ( 20 июля 85 Экранный редактор NS 10 из 10) 1 : EDITCASE ( флаг симв - флаг') 2 : DUP 27 < OVER 0 > AND ( Легальный управляющий символ ?) 3 IF KEYDO ( Если так, то исполняем команду) 4 ELSE DUP 31 > OVER 127


EDITCASE - ключевое слово редактора; это место, где анализируется то, что вводится с клавиатуры, и производятся соответствующие действия. Слово EDITCASE предполагает наличие флага и кода символа в стеке. Сначала анализируется символ и проверяется, лежит ли он в интервале между CTRL-A и CTRL-Z включительно, и если да, то выполняется соответствующая команда. Если это не управляющий символ, его код проверяется еще раз, при этом выясняется возможность отображения через ASCII-код. Если это так, то в зависимости от статуса ввод-замещение производится одно из двух: символ либо вставляется в текст, либо впечатывается поверх того, который был в буфере редактора. Если символ непечатный, он игнорируется. Флаг в EDITCASE служит для управления выходом из редактора. Флаг (0) заносится в стек словом EDITLOOP (описанным ниже) и заменяется на 1, если в ?EXIT (в блоке 6) был сделан выбор "exit" (выход). Слово --- может быть заменено как здесь, так и в KEYVECTORS словом, описанным с помощью CONTROL, чтобы выдать звуковой сигнал, если ваша ЭВМ это позволяет.
Слово EDITCASE суммирует основные операции редактора и является Форт-интерпретацией части, связанной со спецификацией клавиш, приведенной в первой части этой главы. Слово ts ( так было в оригинале -прим. OCR-man)
EDITCASE может быть записано так, чтобы читаться почти так же легко, как словесное описание, если бы оно было написано с использованием трех слов, описанных для того, чтобы исключить три предложения, как это видно из нижеприведенного примера.
ПсевдоФорт из табл 13.2 Действительный текст программы
: EDIT ( n-) BLOCK-TO-BUFFER BLOCK-TO-SCREEN CURSOR-TO-START BEGIN : EDITCASE ( флаг симв-флаг') FETCH-CHARACTER CONTROL-CHAR? DUP CONTROL-CHAR? IF KEYDO IF KEYDO ELSE PRINTABLE-CHAR? ELSE DUP PRINTABLE-CHAR? IF INSERT-MODE? IF INSERT-MODE? IF INSERT IF INSERT ELSE OVERTYPE ELSE OVERTYPE THEN THEN ELSE DROP ELSE DROP --- THEN THEN THEN THEN ; END-FLAG UNTIL ;
Обратите внимание на сходство между EDITCASE и псевдо Форт-спецификацией для редактора в табл.13.2.


Все части оказались взаимно согласованными.
Хотя EDITCASE содержит базовую логику редактора, как показано в табл.13.2, нужно еще много сделать, чтобы редактор был вполне функционирующим. Блок, который нужно отредактировать, должен быть загружен в буфер редактора и должен быть запущен бесконечный цикл приема кодов от клавиатуры, которые нужны для работы EDITCASE. 11 : EDINIT ( blk -) SCR ! EMPTY-BUFFERS LOADBLK SHOWBLK ; Слово EDINIT ("edit-initialize" - вход в редактирование) запоминает номер блока в SCR, очищает блочный буфер с помощью EMPTY-BUFFERS, загружает содержимое блока в буфер редактора и отображает его на экране терминала. 12 : EDITLOOP ( -- ) EDINIT 0 BEGIN KEY EDITCASE DUP UNTIL 13 DROP ;
Слово EDITLOOP запускает редактор с помощью EDINIT и помещает флаг 0 в стек для того, чтобы редактор оставался в бесконечном цикле, пока флаг не будет заменен на 1 в ?EXIT (что заставит UNTIL прервать цикл), после чего этот флаг убирается. 14 : EDIT ( blk -) EDITLOOP ; 15 : E SCR @ EDIT ;
Описание закончено. Чтобы использовать редактор, следует напечатать слова EDIT и E. EDIT устанавливает курсор в исходное положение, очищает экран и входит в EDITLOOP с номером блока в стеке, по завершении редактирования экран вновь очищается. (Если вы хотите, измените EDIT так, чтобы курсор появился под текстом блока, редактирование которого вы прервали по команде QUIT, без стирания экрана.) Слово Е представляет удобный способ возврата к работе с только что редактированным блоком, чей номер хранится в SCR. Если SCR - часть вашей Форт-системы, слово Е перейдет к редактированию блока, для которого последней выполнялась команда LIST. Редактор завершен, если вы, конечно, не хотите его модифицировать. В гл.14 мы увидим,как для редактора можно организовать отдельный словарь, но это сейчас необязательно. Даже если вы найдете этот редактор проще того, который имеется в вашем Форте, наш детальный анализ может помочь вам понять его и, может быть, даже улучшить.
Но наше основное соображение, почему мы дали столь детальное описание редактора, было не просто документирование программы или оказание помощи для модификации вашего собственного редактора: мы надеемся, что вы научились разрабатывать и писать сложные программы.


Этот процесс обсуждается в следующем разделе.
Упражнение
1. Как бы вы отлаживали слова, описанные в блоках 9 и 10? 2. Мы дали два описания EDITCASE, последнее требует описания слов CONTROL-CHAR?. PRINTABLE-CHAR? и INSERT-MODE?. Определите эти слова. Этот процесс выделения части программы и развертывания ее в виде отдельных слов называется факторизацией описания. Подробнее вы прочтете об этом в следующем разделе.
Комментарии
Теперь, когда вы знакомы с текстом редактора и некоторыми причинами того, что она написана именно так, посмотрим на процесс программирования с более общих позиций. Наш первый шаг заключается в формировании четкой идеи основных функций редактора. Это может быть сделано многими путями: с помощью функциональной схемы (рис.13.1), словесного описания (табл.13.1) и псевдо форт-программы (табл.13.2). Для простой программы эти планы можно было бы держать в голове. Но план нужен для того, чтобы направлять усилия при программировании.
Мы не можем чрезмерно подчеркивать важность процесса планирования. Соблазн сесть и написать программу немедленно очень силен, особенно если пользуетесь языком Форт. Лучше обдумать проблему в течение часов, дней или иногда недель. Если вам не терпится сесть за терминал, примите решение по части проекта, которая вам ясна, и приступайте к работе над ней. Например, мы упражняемся с описаниями CONTROL, чтобы проще описать ESC- последовательность. То же самое было сделано, когда мы присваивали функции клавишам в KEYVECTORS. Упражнение с частью программы может быть очень полезным, например, для прояснения общего плана.
Упражняясь с частью программы, вы часто обнаруживаете, что вам нужно отладить слово, которое использует еще неописанные слова. В этом случае полезно использовать слова, называемые подставками. Подставки не выполняют никакой работы, но сообщают вам, что слово исполнено. Например, KEYVECTORS может быть проверено путем замены слов между ] и [ на CRTLA, CTRLB и т.д. до CTRLZ. CTRLA будет подставкой: : CTRLA ." Control A typed" ; KEYVECTORS и KEYDO и даже примитивная версия EDITCASE и EDITLOOP могут быть таким образом протестированы.


Такого рода упражнения могут быть весьма полезны при формировании вашего плана. Мы выполнили это отчасти еще до начала программирования и еще больше в процессе написания редактора. Так как наши основные планы были сформулированы, мы приступили к написанию настоящей программы.
Мы сначала написали редактор с минимальным числом функций для того, чтобы иметь базовую версию, в пределах которой не могли отлаживать новые команды. Таким образом, EDITCASE и EDITLOOP были написаны прежде, чем многие более ранние слова. По мере формирования редактора связанные слова группировались в некоторых блоках, а слова низкого уровня были помещены в первую пару блоков, если необходимость в них становилась очевидной. Когда редактор начал работать, были добавлены и отлажены некоторые новые команды. Процедура программирования состояла из создания слов низкого уровня, от которых все зависит, слов высокого уровня для обеспечения тестовых задач, слов среднего уровня для большинства редактирующих команд (добавления слов низкого уровня по мере необходимости), и в заключение проводились отладка и доводка всего в целом.
Благодаря возможностям, которые предоставляются в сфере тестирования и модификации, Форт идеально подходит для внедрения новых идей в работающие программы. Ключом решения проблемы является разработка базового набора слов. Другими словами, следует разработать удобную номенклатуру слов. Это ключевая концепция программирования на Форте. Номенклатура - это "система наименований, используемых в конкретной области знаний или искусства какой-либо школой или личностью, в частности названий, служащих при классификации для достижения различия с другими техническими терминами (Webster's New Collegiate Dictionary G. & С. Merriam Co., Springfield, Mass., 1959). Спецификация программ - первый шаг в процессе конкретизации проблемы. Следующий шаг - сокращение спецификации до номенклатуры, приемлемой для данной задачи. Если номенклатура зафиксирована, тогда известны слова среднего уровня и остается написать программу, используя эту номенклатуру.


Этот процесс разделения проблемы на более мелкие, которые решить легче, называется факторизацией. Хотя факторизация могла бы быть выполнена на любом универсальном языке ЭВМ с тем, чтобы расщепить проблему на части, с которыми можно работать на этом языке, эта процедура особенно проста и интуитивна на Форте. В Бейсике, Паскале и большинстве других языков факторизация состоит в разработке подпрограмм или процедур, которые вызываются при необходимости. Но в этих языках (в особенности в Бейсике) факторизация часто очень трудна для начинающих. В Форте подход к решению проблем является более интуитивным (и в действительности диктуется самим языком), так как каждое слово Форта является фактором. Каждое слово - это часть номенклатуры, которая разработана в процессе решения проблемы и имеет большое значение в описании решения. В идеале каждое слово воплощает лишь одну идею, так что его использование при построении более сложных концепций в последующих словах довольно легко понять. Слова высокого уровня, хорошо написанной Форт-программы должны читаться почти так же, как словесное описание проблемы. В действительности программа Форта в заметной мере самодокументируется. (Вы видели это в упражнении 2 предшествующего набора задач.)
Существуют различные пути, какими можно факторизовать задачу. В редакторе позиция курсора может быть выражена через число байтов от начала буфера или номера строчки и столбца. Эти два метода представления положения курсора эквивалентны потому, что номера строки и столбца могут быть вычислены на номера байта с помощью операции 64/MOD. Так какой же способ предпочтительнее? Имеются два соображения; положение курсора легче воспринять, зная строку и столбец, да и программировать так легче: строка и столбец используются независимо более часто, чем номер байта в буфере (который можно легко вычислить с помощью ROW и COL). Часто .способ, которым произведена факторизация, сильно влияет на то, как пишется остальная программа. Хотя имена слов в стандартном Форте могут быть весьма информативны (до 31 символа), длина немногих из них достигает даже трети от этой величины.


Почему? По одной причине: длинные имена долго печатать. Может быть, более важно, что они занимают больше место на диске, которого всегда не хватает. Часто сокращения - наилучший путь сохранить информативность имени. Слова базового Форта (такие как @ и !) могут использоваться в именах слов, обозначая извлечение и запоминание чисел (как в @CURSOR и !CLIRSOR), Если слово заносит флаг, включает в себя процедуру выбора или ответа на вопрос, его имя может содержать "?" (как ?BLK# и ?EXIT). И конечно, может использоваться любое число индивидуальных сокращений. Очевидно, баланс должен лежать между чрезмерно длинными словами - описателями и короткими крайне непонятными. Ясно, также, что все сокращения и личные системы кодирования имен слов должны применяться согласованно, что-бы иметь какую-то ценность. Хорошим тестом является проверка можете ли вы читать имена слов вслух, что скажет вам, разумны ли ваши сокращения и, таким образом, будут ли они понятны спустя какое-то время. Другой полезной идеей является присвоение словам имен, которые говорят о том, что они делают, а не как. Важно рассматривать слова Форта как концепцию и часть решения проблемы, а не как часть программы, которая что-то делает. Слова Форта часто проще назвать, если вы отслеживаете их функцию в контексте всей программы. Тезаурус, кстати, может быть таким же ценным инструментом в Форте, каким он является при письме.
Комментарии идут "рука об руку" с хорошими именами слов и имеют целью сделать ваш текст программы понятным как при написании, так и в дальнейшем. Комментарии и состояние стека следует вводить щедро в процессе программирования, чтобы вы могли отслеживать то, что вы ожидаете от ваших слов. Лучше всего вычислить и записать эволюцию стека в слове сразу при его описании, даже если никакого изменения стека не происходит. Эта привычка может весьма упростить задачу ознакомления с вашими новыми словами, так как вы не должны каждый раз просматривать их описания, когда вы их используете.


Конечно, после того, как программа написана, вы должны потратить столько времени, сколько нужно на тщательное оформление вашей программы и блоков, чтобы они были как можно более читаемыми.
Другой полезной привычкой является использование первой строки каждого блока для информации о содержании блока. Информация, обычно включаемая в эту индексную строку, содержит дату последней модификации, заголовок программы, инициалы автора, относительный номер блока и общее число блоков в программе. Некоторые программисты любят перечислять в индексной строке имена слов, описанных в блоке, чтобы облегчить поиск описаний. Порядок размещения этих данных не играет роли, если использованная очередность позволяет выделить важную информацию.
Выводы
Намного легче ремонтировать автомобиль, который на ходу, чем тот, который даже не поставлен на колеса. То же справедливо и для программирования на Форте: как только программа готова, чтобы что-то делать, появляются идеи по ее улучшению и совершенствованию. Если окончательная отладка программы доставляет удовольствие, то полная переделка, когда вы думали, что почти все завершено, - вряд ли. Единственный путь избежать больших задержек при программировании - попытаться предвидеть трудности до того, как они случились. Это делается путем формирования ясной идеи относительно проблемы в целом, прежде чем писать программу.
В любом случае вы должны иметь ясное представление о вашей задаче и целях, прежде чем вы приступите к программированию (на любом языке). Программирование на Форте более творческое, интерактивное и итеративное, чем на других языках. Чарльз Мур создал Форт для своих собственных приложений с целью увеличения личной производительности как программиста. Мы надеемся, что эта глава прояснила, почему форт увеличивает производительность. Форт называют усилителем идей. Существует несколько причин этого. Расширяемость Форта предлагает вам большой выбор. Применение слов Форта разделяет проблему на части, стимулируя логическое мышление.Интерактивная природа Форта способствует быстрой проверке, позволяет вам оттачивать ваши идеи, а использование в Форте длинных имен, аналогичных словам естественного языка, позволяет писать легко читаемые программы. Наконец, Форт дает вам больше власти над ЭВМ, чем может дать какой-либо другой язык. Но было также сказано, что Форт может сделать хорошего программиста великим, а плохого - ужасным. Если вы не будете дисциплинированны в определении вашей задачи, в факторизации и субфакторизации проблем, в определении слов с функциональными именами, в написании хороших комментариев и в расположении вашей программы и блоков разумным образом, вы попадете в категорию ужасных. Надеемся, что эта глава поможет вам двигаться в другом направлении.

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