i21sys/Docu/ru/In.odc

i21sysIn

DEFINITION i21sysIn;
	IMPORT TextModels, TextMappers;
	VAR
		done-, Done-: BOOLEAN;
		scanner: TextMappers.Scanner;
	PROCEDURE Char (OUT ch: CHAR);
	PROCEDURE Int (OUT i: INTEGER);
	PROCEDURE LongInt (OUT l: LONGINT);
	PROCEDURE Name (OUT name: ARRAY OF CHAR);
	PROCEDURE Open;
	PROCEDURE OpenCommander;
	PROCEDURE OpenFocus;
	PROCEDURE OpenSelection;
	PROCEDURE OpenText (text: TextModels.Model; beg, end: INTEGER);
	PROCEDURE Real (OUT x: REAL);
	PROCEDURE Restart;
	PROCEDURE String (OUT str: ARRAY OF CHAR);
END i21sysIn.

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

Модуль позволяет использовать любой фрагмент текста в любом из окон в качестве входных данных для очередной команды (будем называть такой фрагмент текста потоком ввода; см. ниже описание процедур Open***). 
То есть поток ввода может определяться интерактивно, что удобно как в учебном контексте, так и в прикладных задачах. 

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

Подобный, но ограниченный и гораздо менее надежный механизм встроен в такие излюбленные физиками системы, как PAW или ROOT, притом что там используются интерпретаторы для дописываемых процедур.

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

Полезно иметь в виду, что scanner можно в своей программе напрямую подсоединить к любому тексту (объекту типа TextModels.Model), в том числе загруженному из файла и не показанному ни в каких окнах.

Основой для данного модуля послужил блэкбоксовский модуль In, который, однако, не является для Блэкбокса родным, а был взят из книжки Райзера и Вирта ("Programming in Oberon" by Reiser/Wirth), чтобы первые пользователи Блэкбокса могли использовать ее в качестве учебного пособия. При этом в версии Вирта и Райзера была одна ошибка (потеря точности при вводе вещественных чисел), которую авторы Блэкбокса намеренно оставили, чтобы примеры из книжки воспроизводились буквально. Кроме того, модуль In не согласуется с Соглашениями по оформлению программ, принятыми в Блэкбоксе, что неудобно с учебной точки зрения. Наконец, для оригинального модуля In концом потока ввода является конец документа, а не конец выделенного фрагмента, что оказалось неудобным.

По этим причинам вместо модификаций модуля In был сделан независимый модуль, i21sysIn, который почти полностью воспроизводит модуль In, отличаясь следующими усовершенствованиями:
 упомянутая ошибка с округлением исправлена;
 интерфейс приведен в соответствие с Соглашениями, т.е. наряду с булевской переменной Done введена переменная с правильным именем done, при этом всегда гарантируется тождественное равенство значений done = Done);
 входной поток теперь определяется более удобными и общими способами (во-первых, не обязательно выделять его мышкой, во-вторых, фрагмент текста, который нужно сделать потоком ввода, может находиться в любом документе, в-третьих, поток ввода копируется в специальный буфер и поэтому защищен от модификаций);
 расширены возможности чтения входного потока (команда Restart возвращает в начало входного потока; кроме того, экспортирован весь сканер, подключенный ко входному потоку, так что все средства сканирования входного потока теперь доступны из модулей-клиентов).

Примеры можно найти в модуле i21sysInПримеры (чтобы открыть исходник, дважды кликнуть по имени модуля и нажать Ctrl+0; коммандеры для примеров находятся в конце документа за текстом модуля).

VAR done-
Значение этой переменной показывает, была ли успешной последняя операция из числа операций ввода, определенных в этом модуле. Значение done устанавливается в TRUE при успешном вызове Open или Restart, и устанавливается в FALSE первой неуспешной операцией ввода. Однажды установленная в FALSE, она сохраняет это значение до следующего вызова Open или Restart.
NB Прямые обращения к методам сканера scanner не влияют на значение done. Поэтому для чтения потока ввода нужно либо использовать исключительно scanner, либо не использовать его вообще. 

VAR Done-
Всегда Done = done. Эта переменная сохранена для совместимости учебных программ, использующих старую версию модуля In.

VAR scanner
Объект, выполняющий работу по сканированию потока ввода. 
Подсоединяется к копии вводимого текста при выполнении процедуры Open (см. правила определения вводимого текста в описании процедуры Open).
Использовать scanner следует лишь в тех случаях, когда требуется детальный (посимвольный) анализ структуры вводимых данных, например, когда структура вводимых данных неизвестна или слишком сложна.
Если же структура вводимых данных известна и достаточно проста, то следует избегать обращений к scanner напрямую и по возможности использовать процедуры, определенные в данном модуле.

Все процедуры Open*** , описанные ниже, открывают входной поток, соответствующий некоторому фрагменту текста, отличаясь способом определения фрагмента.
Во всех случаях, если текстовый фрагмент на роль потока ввода определен успешно, то создается его копия, к ней подсоединяется scanner, а done устанавливается в TRUE.
В противном случае done устанавливается в FALSE.

При этом концом потока ввода во всех случаях считается первая встреченная во фрагменте вьюшка или, если таковая отсутствует, конец фрагмента. 
Удобно помечать конец вводимых данных, вставляя специальный треугольничек  нажатием Ctrl+Shift+Q.
Это правило (про окончание вводимых данных на первой встреченной вьюшке) можно обойти, если читать входной поток, обращаясь непосредственно к процедурам объекта scanner.

PROCEDURE OpenCommander
Открывает поток ввода, состоящий из текстового фрагмента, следующего за именем процедуры, вызываемой с помощью коммандера. 

PROCEDURE OpenFocus;
Открывает поток ввода, начало которого совпадает с началом текста в переднем (верхнем, фокусированном) окне (см. программу, печатающую себя: Mod/Self).

PROCEDURE OpenSelection
Открывает поток ввода, состоящий из текстового фрагмента, выделенного в данный момент в переднем (верхнем, фокусированном) окне.

PROCEDURE Open
Открывает поток ввода по следующему алгоритму:
 сначала вызывается OpenSelection (т.е. проверяется наличие выделенного фрагмента).
 в случае ~done вызывается OpenCommander.

PROCEDURE OpenText (text: TextModels.Model; beg, end: INTEGER);
Для использования этой процедуры необходимо понимание принципов построения каркаса Блэкбокса вообще и знакомство с подсистемой Text в частности. В качестве примера использования см. процедуру OpenSelection в исходнике обсуждаемого модуля.
Открывает поток ввода аналогично Open, но использует фрагмент текста text от позиции beg по позицию end-1 включительно. 

PROCEDURE Restart
Возвращает процесс сканирования в начало потока ввода, если он был успешно открыт.
Эта процедура обеспечивает полное разделение операции определения потока ввода и операций его обработки. В результате можно определить поток ввода как часть текста в одном окне (например, командой меню Text, Open Input или Shift+F1), а команду обработки потока ввода вызывать коммандером, находящимся в другом окне. Такая команда обработки обычно должна использовать Restart вместо Open.
Это более гибкий и универсальный способ работы с вводимыми данными.

Постусловие
done
	поток ввода открыт и процесс сканирования начнется с начала
~done
	поток ввода не был открыт до этого вызова Restart.


Операции чтения потока ввода

Дальнейшие операции подразумевают открытый поток ввода.
Пользователь должен знать порядок следования элементов данных, так как ошибочная операция вызывает Трап.
Если такой информации нет, то следует использовать scanner.

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

PROCEDURE Char (OUT ch: CHAR)
Если done, эта процедура пытается прочесть очередную литеру в потоке ввода, иначе она ничего не делает.

Постусловие
done
	литера была успешно считана в ch
~done
	попытка чтения не удалась (встретился конец потока ввода или вьюшка)

PROCEDURE Int (OUT i: INTEGER)
Если done, эта процедура пытается прочесть целое число, иначе она ничего не делает.

Постусловие
done
	целое число было успешно cчитано в i
~done
	попытка чтения не удалась (т.е. либо встретился конец потока ввода, либо там стоит что-то другое, что означает ошибку в программе или во вводимых данных)

PROCEDURE LongInt (OUT l: LONGINT)
Если done, эта процедура пытается прочесть длинное целое, иначе она ничего не делает.

Постусловие
done
	длинное целое было успешно cчитано в l
~done
	попытка чтения не удалась (т.е. либо встретился конец потока ввода, либо там стоит что-то другое, что означает ошибку в программе или во вводимых данных)

PROCEDURE Real (OUT x: REAL)
Если done, эта процедура пытается прочесть вещественное число, иначе она ничего не делает. Процедура работает корректно, если в потоке ввода обнаруживается целая константа или константа типа LONGINT.

Постусловие
done
	вещественное число было успешно cчитано в x
~done
	попытка чтения не удалась (т.е. либо встретился конец потока ввода, либо там стоит что-то другое, что означает ошибку в программе или во вводимых данных)

PROCEDURE Name (OUT name: ARRAY OF CHAR)
Если done, эта процедура пытается прочесть имя, иначе она ничего не делает. 
Имя  это последовательность допустимых идентификаторов языка Компонентный Паскаль, соединенных точками, например, "Dialog.Beep".

Постусловие
done
	Имя успешно считано в name
~done
	попытка чтения не удалась (т.е. либо встретился конец потока ввода, либо там стоит что-то другое, что означает ошибку в программе или во вводимых данных)

PROCEDURE String (OUT str: ARRAY OF CHAR)
Если done, эта процедура пытается считать цепочку литер, иначе она ничего не делает. Цепочка литер  это последовательность литер, ограниченная либо белыми литерами (пробелы, концы строк, табуляции), либо двойными кавычками ("). 
Если цепочка должна содержать пробелы (концы строк, табуляции), то ее следует взять в двойные кавычки.

Постусловие
done
	цепочка литер успешно считана в str
~done
	попытка чтения не удалась (т.е. либо встретился конец потока ввода, либо там стоит что-то другое, что означает ошибку в программе или во вводимых данных)


Замечания 

О кириллице. Если возникают проблемы с чтением кириллических букв из потока ввода, полученного копированием текста из других программ (текстовых редакторов и т.п.), то это может иметь место из-за того, что кириллические литеры вставлены в формате Unicode (два байта на литеры). Проблема обычно решается, если делать вставку в документ Блэкбокса посредством "специальной" вставки (Paste Special...) с выбором формата Plain Text. 

Как открывать потоки ввода с помощью меню или горячей клавиши. Команду Open по смыслу нужно включать в меню и привязать к горячей клавише (например, Shift+F1).
Например, в документ, описывающий меню Text (чтобы открыть его, кликнуть сюда:Text/Rsrc/Menus), можно сделать следующие добавления:

Один раз в раздел 
MENU "Text" ("TextViews.View") 
можно вставить строку
	"Open Input"	"*F1"	"i21sysIn.Open"	"TextCmds.FocusGuard"
это обеспечит появление команды в меню Text
Для появления команды в контекстном меню, вызываемом кликом правой кнопкой по выделенному фрагменту, нужно точно такую же строку добавить в том же документе в раздел
MENU "*" ("TextViews.View")
После сохранения документа меню активизируется командой Info, Update Menus


info21, версия 2012-09-21