| Предыдущая глава |
↓ Содержание ↓
↑ Свернуть ↑
| Следующая глава |
начинает "распространятся" — последовательно прекращая выполнение вложенных
вызовов подпрограмм. И дораспространявшись до интерпретатора, вызывает с его
стороны нелицеприятную реакцию. Но буде одна из подпрограмм поставила
подходящую ловушку — она срабатывает; запускается подпрограмма реакции, которая
якобы и исправляет ошибку. После чего столь предусмотрительная подпрограмма
нормально завершается — возвращает управление в точку вызова.
Искусственно порождать ситуации поручено оператору Quit (а события — Kill).
Например так можно досрочно выскочить из цикла или даже нескольких вложенных:
Brek 7; For i=1,N; For j=-M,M; For k=0,L; If(FSBr(ххх, i,j,k))0; Quit 7,""
Вот как подпрограмма по адресу ххх вернёт положительное число, показывая что
переданные ей i j k её вполне устраивают, так и. Это и есть "структурный
переход". Ситуация 7 выбрана "от балды" (лишь бы не совпадала с уже имеющимися:
теми, которые ошибки). Дололнительные числовые аргументы в Quit передаются
подпрограмме реакции, но она в этом примере — "нулевая". А вот текстовый -
интерпретатору, чтобы он, если что, знал что сказать. А в нулевой строке (как
в данном случае) указывает — что мол еще поработаем, что это не команда на
завершение работы интерпретатора.
Если вышеприведённый пример в одну строку, таки строка подпрограммы, то
следующие после неё строки не будут выполнены: ведь тут же случилась СИТУАЦИЯ,
но реакция на оную (хотя и нулевая) героически устранила последствия. После
чего подпрограмма НОРМАЛЬНО завершается, а не... (Типа: не устрани она — ой что
было бы!) Ага-ага. Но если нам надо чтобы выполнились и следующие строки, надо
засунуть всё это в персональную подпрограмму, сделав телом оператора For из
одного только этого ключевого слова. Вот пусть это она "нормально завершается".
— В дополнение к трассировке и пошаговому режиму, которые были всегда, с
одной стороны отладочное окошко (правда весьма ограниченной полезности, так как
пристойно работает только в текстовых режимах) — чтобы трассировочная
информация попадала туда и не на основной экран и не портила изображение;
а с другой — микро-хелп в отладочном режиме (подсказка по его командам) и
возможность ввести и выполнить командную строку — чтобы например посмотреть
значения переменных или заглянуть в основной хелп...
А вот перенаправление трассировочной информации в отдельный файл — тот,
который указывается в командной строке вторым и открывается под псевдонимом Б,
так и не сделано: уж больно как-то оно всё неизящно получается...
— Табулостопы и спецкомментарий из одних двоеточий, типа: : : : !
где двоеточиями отмечаются их позиции. Спецкомментарий становится "спец" только
если завершается ! (восклицательным знаком) или ; (точкой с запятой),
указывающими на конец строки или же её продолжение. Иначе, как и любой
комментарий, игнорируется. (Раньше двоеточие тоже числилось буковкой на ряду со
всякими # @ $ | ~ но поручив ему табуляцию, пришлось назначить
его разделителем.)
Вобще-то табулостопы понадобились оператору Write со вторым ключевым
словом Set, сообщающему результат деятельности оператора присваивания — список
переменных, имеющихся на настоящий момент в памяти. Вместе с их значениями,
разумеется. Этих переменных может быть как две-три, так и десятки, сотни и
даже тысячи, особенно когда мы имеем дело с массивами. И выдавай оператор Write
каждую из них в отдельной строке — просматривать всё это было бы муторно.
Надо чтобы в виде таблички, желательно стройными рядами и колоннами... Ну так
на то и табуляция!
Секретарши испокон веку печатали на пишущих машинках всяческие таблички.
И у них там эти самые "табулостопы" — такие выдвигающиеся железочки,
отмечающие начало очередной колонки. А клавиша ТАБ передвигала каретку к
следующему. Но в эпоху алфавитно-цифровых дисплеев их тупо расставили в каждую
восьмую позицию... А я сперва сделал чтобы можно было устанавливать какой хочу
шаг табуляции, а потом и оные табулостопы индивидуально. И свалил всё это на
FVIz. Получилось универсально, но таки сложновато и ненаглядно. А наглядно — в
виде вот такого спецкомментария. Он же выдаётся по Write Tab; а Eraze Tab;
приводит систему с состояние по-умолчанию.
И теперь вот думаю: а ведь запись в базе данных, ну например типа .dbf,
она ведь вот так же выглядит? Правда, там у полей есть имена, и их границы
пересекать не полагается... А что мешает и мне так сделать? И можно будет
заполнять поля оной записи в произвольном порядке, типа: Type :имя, значение;
Правда, для этого понадобится еще и буферизация вывода...
* * *
Подсистема ввода/вывода Фокала (ну наконец-то мы до неё добрались!)
устроена очень просто: есть один канал ввода и один канал вывода. Канал вывода
обычно направлен на терминал, за которым и сидит пользователь, но может быть
переключен оператором Operate куда-то еще. А канал ввода соответственно
по-умолчанию подключен к клавиатуре.
Из канала ввода читает функция FCHr — побайтно, оператор Ask — построчно,
и интерфейсная часть интерпретатора (тоже построчно), когда ей нужна
очередная командная строка.
Обратим внимание: интерфейсная часть интерпретатора читает командные
строки из канала ввода, не интересуясь — к чему именно он подключен. Что и
даёт возможность как загрузить ранее сохранённую программу (просто переключив
на неё канал ввода — будет вводиться сама собою, пока не кончится...), так и
выполнять командные ("скриптовые") файлы — в точности так же. Отличаются
только тем, что тут все строки — "прямые", а там — косвенные. То есть ничем:
ничто не мешает как добавить после программы прямую строку, которая например
её сразу же и запустит, так и командному файлу по мере необходимости
подгружать (а потом — удалять) куски программ. (Сильно сомневаюсь, чтобы
Бейсику было доступно хоть что-то подобное.)
Кстати, при запуске интерпретатора, первый аргумент в командной строке
это имя файла, который сразу же открывается (под псевдонимом "А") и на него
переключается канал ввода...
В канал вывода пишут все, кому не лень: и та же самая функция FCHr — тоже
побайтно; и операторы Type, Write, Help; и сам интерпретатор — сообщения об
ошибках; и даже функция FTMp. (Прямо не канал, а проходной двор!)
Но за то, в отличии, например, от Бейсика, где и для вывода программы
"на посмотреть" пользователю, и для сохранения её где-то на внешнем носителе
используются разные команды — LIST и SAVE, каждая со своим собственным
заковыристым и неочевидным синтаксисом, то в Фокале всё это делает один и тот
же оператор Write, синтаксис которого предельно прост. Но конечно для
сохранения программы сначала надо переключить канал вывода туда где сохраняем,
а потом обратно. А если это файл, то перед этим надо его открыть, а после -
закрыть. Но это-то как раз очевидно, хотя и громоздко. Ну так для облегчения
жизни (самому себе) при попытке выйти из интерпретатора нажав ESC, выдаётся
уже готовая командная строка для сохранения программы в файле "sav.f". (Но
только если программа — менялась.) Потому как исправить готовое куда легче чем
каждый раз набирать заново. (А при следующем запуске, если явно не указали
какой файл открывать, попытается открыть вот этот — тоже под псевдонимом А.)
Список вывода оператора Type содержит не только выражения, значения
которых и надо преобразовать в удобочитаемый вид, и таинственную конструкцию
формат, начинающуюся с символа % (процент), указывающую как именно это делать,
но так же еще и текстовые константы, призванные служить пояснениями
к выводимым числам. Это любые символы в кавычках, ! (восклицательный
знак), предписывающий учинить конец строки, а теперь еще и : (двоеточие),
указывающее на переход к следующему табулостопу. Если в выводимом тексте
должны быть кавычки — его следует заключить в кавычки другого типа, благо их
три: " ' `
Аналогично, список ввода оператора Ask тоже может включать текстовые
константы, призванные служить "приглашениями" ко вводу очередного числа. Ну а
коли это приглашения, то я сделал что выводятся они только и исключительно на
терминал (вне зависимвости от того куда направлен канал вывода) и только в
случае когда канал ввода направлен на клавиатуру.
Терминал с клавиатурой — вещь особая: там, при выводе задействуется
просмотрщик (для Write и Help — автоматически, а для Type — "вручную", с
помощью "спецформата" в виде пары символов %), а при построчном вводе -
редактор командной строки. А когда он не задействован — ввод попадает в
односимвольный буфер клавиатуры — специально для FCHr. А еще у экрана есть
курсор, указывающий текущую позицию, и память, где хранится то что на нем
нарисовано. (Всё это в ведении функции FKursor.)
Но этого может и не быть — терминал может, как в древние времена,
оказаться чисто механическим телетайпом, украденным на почте...
Файл, впрочем, штука тоже по-своему особенная: от клавиатуры или какой
либо линии связи отличается тем, что имеет "текущую позицию". И вообще может
быть представлен в виде длинной (но конечной) ленты, разделенной на клеточки,
одну из которых "обозревает" головка чтения/записи. Которая при любой операции
автоматически сдвигается на следующую. Но может быть принудительно установлена
куда нам вздумается. Это логично поручить тому, кто и оперирует с файлами
(оператору Operate, разумеется), но вот сообщить текущую позицию он не сможет,
для этого нужна функция. Ввёл, и по аналогии с Си-шной назвал FTEll. (Надо было
FOper называть! Хотя еще не вечер...) И это была вторая важная вещь, которой и
характерна "часть первая". (Потому что всё остальное — удобства и красивости.)
А позицию в каком из уже открытых файлов? Решил, что в том, который
участвовал в последней по времени операции ввода или вывода.
Намотаем на ус: где-то там, в недрах Фокала специально для FTEll завелась
скрытая переменная, куда попадает объект любой операции ввода/вывода так же,
как в аккумулятор А1 попадает значение (почти) любого вычисленного Фокалом
выражения. Забегая вперёд, скажем, что это и есть аккумулятор А3.
Оперирует каналами ввода и вывода оператор Operate ключевое_слово;
где вот это второе ключевое слово и указывает на одно из подключенных к ЭВМ
устройств ввода-вывода, или (сейчас) еще и на файл. И тогда называется
псевдонимом открытого файла, так как выделяется для него временно — только
пока он открыт. Как известно, чтобы добраться до содержимого ящика, сундука,
шкафа, потрфеля... ну и файла, разумеется — их сперва надо открыть, а
попользовавшись, закрыть обратно. Файлов одновременно можно открыть штук
несколько. (Но не очень много: таблица открытых файлов не резиновая).
Некоторые ключевые слова испокон веку закреплены за устройствами и
использовать их под псевдонимы нежелательно. К терминалу (Tty) и клавиатуре с
принтером (Kbd, Lpt) добавились еще I, O, E — стандартный ввод, вывод и вывод
ошибок, которые, по традиции UNIX`а открыты у любой программы уже в момент её
запуска. (И обычно указывают на те же самые клавиатуру и терминал. Но запросто
могут оказаться куда-то перенаправлены...) И плюс к ним ко всем еще и
C — стандартный компорт, который ДОС тоже почему-то предоставляет каждому
запущенному процессу, нимало не интересуясь, нужен он ему или нет.
Файлы открываются (и закрываются): Ope "имя_файла" (позиция) Псевдоним;
(закрываются если имя файла — пустое). Позиция (число в скобках) при открытии
не обязательна, но если есть — файл будет открыт с неё, а не с начала. Только
позиция без имени файла — вот это самое позиционирование. В том числе и
фиктивное (если в скобках пусто) — для последующего обращения к FTEll.
В отличии от всех других ключевых слов, в псевдониме не все "лишние"
буквы игнорируются: некоторые указывают как именно открыть файл (и/или какие
каналы переключать): R W A (последняя — тоже на запись, но в конец) и тип
файла: B — бинарный, T — текстовый.
Если буферизация вывода еще только-только намечается, то на ввод есть два
буфера — у оператора Ask и у самого интерпретатора: они оба читают за раз сразу
целую строку, а вот используют её — постепенно. Интерпретатор выполняет по
одному оператору, а Ask — вводит по одному числу, вернее вычисляющему его
выражению...
Так, еще раз, и помедленнее: оператор Ask, встретив в своём списке ввода
очередную переменную, ВЫЧИСЛЯЕТ полученное из канала ввода ВЫРАЖЕНИЕ, а не
просто берет оттуда число и преобразует во внутреннее представление, как во
всех других языках! Где еще подобное видано?! Фокал — калькулятор, однако...
Но вернемся к нашим баранам.
Входной буфер интерпретатора позволяет в пределах одной "прямой" строки
выделывать с каналом ввода всякие фокусы — это никак не влияет на её выполнение
и активно используется, например, для управления порядком выполнения командного
("скриптового") файла. Ведь там, например для обращения к подпрограмме, надо
перемотать файл на её начало, где-то сохранив при этом адрес возврата. Если с
местоположением подпрограммы — большой вопрос (но пусть мы его откуда-то знаем
и уже храним в переменной П1), то с адресом возврата всё просто: текущая строка
уже целиком в буфере; указатель чтения/записи — на начале следующей, которой и
надо будет вернуть упраление. Поэтому, считая, что командный файл у нас — под
псевдонимом А, делаем так: Op () A; Set Ав=Ftell(); Op (П1) A
А возврат из такой подпрограммы: . . . . . . . . . . Op (Ав) A
И всё.
А вот с буфером оператора Ask явно что-то не так...
Забегая вперёд, скажем, что эта штука, предназначенная для временного
хранения текстовой строки — искомый аккумулятор А2, позволяющий Фокалу работать
со строчками. (Вот только делать с ним мы ничего пока не умеем.)
* * *
И с этого места у нас начинается ЧАСТЬ ВТОРАЯ нашего повествования
* * *
Не будем тянуть кота за хвост, а сразу возьмём быка за рога: желаем
работать с текстовыми строками. Но сами тексты — слишком громоздкие. Пусть
так и лежат снаружи в виде текстовых файлов...
(Впринципе мы и раньше могли вводить и выводить тексты посимвольно — с
помощью спецфункции FCHr, а хранить — в массиве в виде последовательности
чисел. Да, стрельба из пушек по воробьям, но на безрыбье и рак — рыба...)
Одно из применений — самомодификация. Вот например: мы никак не можем
открыть файл, имя которого неизвестно заранее — оно вишь указывается оператору
| Предыдущая глава |
↓ Содержание ↓
↑ Свернуть ↑
| Следующая глава |