Предыдущая глава |
↓ Содержание ↓
↑ Свернуть ↑
| Следующая глава |
значения" операторов. (Например счетчик строк, реально скопированных
оператором W %;.)
Второй: (А2) "строчный" аккумулятор — входной буфер оператора Ask. (Который
вишь вводит за раз целую строку, а использует её — постепенно.) Вокруг А2
постепенно сам собой нарос механизм работы с текстовыми строками. (См. далее.)
Существенно связанный с подсистемой ввода/вывода. Потому что считается что
тексты (как совокупность строк) слишком громоздки, и могут храниться только
снаружи, например в виде файлов.
Третий аккумулятор (условно "А3") — это та внутренняя переменная, куда
попадает указатель на файл, поучаствовавшей в последней по времени операции
ввода/вывода (в т.ч. и фиктивной) чтобы функция FTEll могла сообщить про него
какую-то информацию. И он нам тоже еще пригодится...
* * *
ЛОКАЛЬНЫЕ ПЕРЕМЕННЫЕ
В базовом Фокале, при обращении к подпрограмме с помощью спецфункции
FSBR можно передать только один параметр. Попадающий в переменную с именем &,
которая, по всей видимости, была глобальной. То есть при рекурсивном вызове
такой функции, предыдущее её значение безвозвратно терялось.
В данной реализации разрешается передавать подпрограмме произвольное
количество параметров. В том числе и при обращении к ней оператором Do.
А также при т.н. "структурном переходе" — порождении ситуации оператором
Quit (см. далее).
Эти (позиционные) параметры помещаются в настоящие локальные переменные,
доступные по именам & (или &0 или $ или $0), &1, &2, &3... (или $1, $2, $3...)
или как элементы одномерного массива &(...) включая и такие как &(-1), куда
обычно помещается количество переданных параметров.
Локальные переменные создаются не только при передаче параметров, но и так
же как и глобальные — при первом присваивании. Но в отличии от них, сперва
ищутся у подпрограмм с меньшей степенью вложенности. И обращение к
несуществующей локальной переменной ошибки не вызывает.
* * *
УСОВЕРШЕНСТВОВАНИЕ ОПЕРАТОРОВ
В операторе For:
— если шаг не указан, то он принимает значение +1 или -1 в зависимости от
того, больше конечное значение начального или меньше
— цикл завершается когда параметр цикла выйдет за конечное значение более чем
на пол шага. (Чтобы правильно работал и в случае когда шаг — не целый.)
— после окончания цикла его параметр, как и полагается, сохраняет то значение,
которые было на момент последней итерации.
— допускается форма из одного только ключевого слова: For;. Тогда остаток
строки выполняется ровно один раз, но как подпрограмма. (Это — в основном для
ограничения области действия структурного перехода — см.далее.)
В операторе If:
— допускается отсутствие не только последних, но и любого из адресов перехода
(при условии сохранения разделяющих их запятых) — в соответствующем случае
выполняется остаток строки
— допускается нулевой адрес перехода — в соответствующем случае выполнение
текущей строки просто завершается
— допускается отрицательный адрес перехода — он эквивалентен пропуску: в
соответствующем случае выполняется остаток строки.
Всё это — чтобы использовать оператор вообще без адресов. Например:
If (x) ,0; t "икс не равно нулю"!
If (x) 0, ,0; t "икс равно нулю "!
— из трёх адресов перехода вычисляется только один. Остальные — пропускаются.
Потому, что это может быть выражение общего вида, в том числе дающее побочные
эффекты.
Оператор Go
— с нулевым адресом передаёт управление на начало текущей строки — для
организации цикла без счетчика. Например:
...тело цикла...; If(Усл)0; G 0; Циклится пока Усл >= 0
Оператор Do
— кроме адреса перехода может содержать произвольное количество выражений.
Они все вычисляются и дают начальные значения локальных переменных &, &1, &2...
для вызываемой подпрограммы.
— некоторые из этих выражений могут быть пропущены — при наличии разделяющих
их запятых
— адрес перехода тоже может отсутствовать — при наличии отделяющей его от
следующих выражений запятой. Тогда никакая подпрограмма не вызывается, значения
попадают в локальные переменные текущей подпрограммы.
— НО: оператор Do (или Go) состоящий из одного только ключевого слова — для
запуска всей программы — исключение. Может быть в строке только единственным
(или последним, и даже ; после него не допускается), иначе вызывает ошибку.
Оператор Ret
— допускает одно выражение, вычисляющее возвращаемое значение подпрограммы.
Оно попадает в числовой аккумулятор (А1) и, возможно, возвращается с помощью
функции FSUbr. Ret Х; эквивалентно Хecut Х; Ret;
Функция FSUbr так же как и оператор Do
— допускает произвольное количество аргументов, а так же пропуск любых из них.
— первый аргумент, как и раньше — адрес для передачи управления подпрограмме,
остальные становятся начальными значениями её локальных переменных &, &1, &2...
— при пропуске первого аргумента (как и Do) никакой подпрограммы не вызывает;
сразу возвращает текущее содержимое аккумулятора А1. Остальные аргументы, если
есть, попадают в локальные переменные текущей подпрограммы.
Оператор Do и ф-я FSUbr передают управление точно, а Go и If — примерно.
Отсутствие строки с указанным номером в первом случае вызывает ошибку, а во
втором — передачу управления на следующую. (В пределах статуса выполнения!!!)
Операторы Set и Хecut
— допускают несколько выражений в операторе X и несколько присваиваний в
операторе Set — через запятую. Выпендрёж чистой воды.
Оператор Quit
— допускает один или несколько аргументов: первый — номер ошибки (ситуации),
остальные — параметры, которые будут переданы подпрограмме реакции на эту
ситуацию (см. ниже).
— допускается чтобы один из этих аргументов был текстовой константой. Это
сообщение о данной ошибке сидящему за терминалом человеку. Для оператора Quit
выполняющегося в "прямой" (нулевой) строке наличие такого аргумента блокирует
завершение работы интерпретатора. Примечание: в командном файле все строки
"прямые"...
* * *
Для операторов Write и Eraze в базовом Фокале понимавших только одно
ключевое слово Ales — "всё", добавлены дополнительные ключевые слова,
совпадающие с названиями операторов, результаты работы которых надо показать
или удалить. (Чтобы не спутать с ними выражение, начинающееся с имени
переменной, его всегда можно заключить в лишние скобки.)
— Open — список всех открытых файлов
— Set — список всех переменных
— Do — состояние операционного стека, включая локальные объекты
— Break — список ловушек на события
а так же
— Reg — содержимое регистров форматного механизма (см. далее)
— Tab — таблица табулостопов (в виде спецкомментария — см. далее.)
— Pul или Мешок — Write ничего не сообщает, а только помещает содержимое
строчного аккумулятора в пул (мешок) ранее введенных строк редактора командной
строки; Eraze делает его пустым.
Но "Set", "Do" и "Open" — только для Write, потому что переменные оператор
Eraze и раньше стирал без всяких ключевых слов, а содержимое стека возвратов
стереть нельзя впринципе. Файлов Eraze тоже закрывать не уполномочен...
(Только когда Eraze Ales; — сразу все, но кроме тех двух, на которые
направлены каналы ввода и вывода.)
Регистры форматного механизма E R; очищает, а вот аккумулятор А2 — нет.
(Как впрочем и А1 и А3 тоже не очищаются ничем.) Но он очищается автоматически
(если не запретить) при переключении канала ввода, и "вручную": Ask %
(см. далее).
Операторам Write и Eraze дополнительно поручено выдавать и соответственно
стирать не все переменные какие есть, а только некоторые. (Например элементы
одного массива.) Указанные текстовой константой в качестве аргумента:
Write "имя"; Eraze "имя";
* * *
СИТУАЦИИ И СОБЫТИЯ
При возникновении любой ошибки, базовый Фокал, будучи фактически
программируемым калькулятором, останавливал выполнение программы и передавал
управление человеку, сидящему за терминалом. В настоящей версии на этот
случай реализован "механизм ситуаций".
Ситуация, возникшая в результате ошибки или искусственно созданная
оператором Quit, начинает "распространяться". И если никакой реакции на неё
небыло предусмотрено, то, как и раньше, останавливает работу программы с
выдачей сообщения об ошибке. И переключением каналов ввода и вывода на
терминал и клавиатуру. Передавая тем самым управление человеку за терминалом.
Но любая из подпрограмм может поставить на данную ситуацию "ловушку". Если
ситуация так и не возникла, то это не окажет никакого влияния на ход выполнения
программы: когда подпрограмма завершится, ловушка будет просто удалена так же,
как и принадлежащие этой подпрограмме локальные переменные. А вот если и когда
возникшая где-то во вложенных подпрограммах ситуация дораспространяется до
этого места — ловушка сработает, будет выполнена предусмотренная в ней реакция
(в том числе "нулевая") и столь предусмотрительная подпрограмма завершится
нормально, как будто никаких ошибок небыло. Это и есть "структурный переход".
Позволяющий, в частности, досрочно выйти из цикла. Или даже нескольких
вложенных. Например нам нужно найти в матрице a() элемент a(i,j) значение
которого больше или равно k. Полезный результат — вот как раз индексы i и j,
которые здесь параметры циклов.
5.4 B 17; F i=1,N1; F j=1,N2; I(a(i,j)-k) 0; Q 17
Да, параметры циклов можно было сохранить во вспомогательных переменных и
установить равными конечным значениям N1 и N2. Но вот так — изящнее.
Номер ситуации 17 здесь выбран "от балды", главное: ошибок с таким номером
заведомо нет.
Обратим внимание: строкой 5.4 выполнение подпрограммы в группе 5 и
закончится. Потому что срабатывание ловушки ведёт к выходу из ближайшей
подпрограммы. Если нам надо, чтобы после 5.4 выполнялись так же строки 5.5,
5.6, 5.7... можно ограничить распространение ситуации одной только текущей
строкой — следующим образом:
5.4 F; B 17; F i=1,N1; F j=1,N2; I(a(i,j)-k) 0; Q 17; C см. оператор For
Ошибки (ситуации), так же как и номера программных строк, объединяются в
группы, чтобы можно было поставить ловушку как на всю группу ошибок сразу, так
и на конкретную. Группы ошибок:
0 — действия человека за терминалом (например 0.3 — нажата Ctrl/C)
1 — синтаксические ошибки (например 1.4 — дисбаланс скобок)
2 — арифметические ошибки (например 2.1 — деление на ноль)
3 — ошибки ввода/вывода (например 3.2 — конец файла)
4 — прочие ошибки (например 4.3 — нет переменной)
5 — недостаток ресурсов (например 5.1 — не хватает оперативной памяти)
* * *
Реакцию на внешние по отношению к программе события тоже устанавливает
оператор Break. События отличаются от ситуаций тем, что их номера -
отрицательные, ловушки на них — глобальные, и реакция не может быть "нулевой":
если указана нулевая реакция — отслеживание данного события отменяется.
Фокал — интерпретатор медленный, поэтому внешние события именно что
"отслеживаются": с каждым из них связан счетчик, при возникновении события он
наращивается. А когда запускается подпрограмма реакции — передаётся ей в
качестве одного из аргументов и обнуляется.
Реакция на событие (аналог прерываний) запускается "вне очереди" между
выполнением операторов основной программы. Тоже как подпрограмма. И на время
её работы реакция на другие события запрещается. (А вот если она сама вызовет
подпрограмму — там уже можно.)
Следует иметь в виду, что некоторые операторы могут выполняться очень
долго. Например включающие ожидание ввода с терминала. В том числе когда
"интерфейсная" часть интерпретатора ожидает ввода очередной командной строки.
Реакция в это время, разумеется, запущена быть не может, но события -
отслеживаются.
События искусственно порождаются оператором Kill. Первые 256 из них это
реальные машинные прерывания. (Поэтому: обращаться осторожно!) События с
номерами от 1000 — внутренние. Например 1003 — нажатие комбинации кнопок
Ctrl/C, а 1004 — окончание звукового фрагмента. Имеется так же возможность
заказать события с помощью функций FViz и FCLk — на действия с мышкой в
пределах указанной области и на истечение временного интервала (или с
указанным периодом).
Так как некоторые из прерываний — обращения к ДОС`у и БИОС`у,
предполагающие передачу параметров (через регистры процессора), то в операторе
Kill допускается произвольное количество дополнительных параметров. Первые из
которых раскладываются по регистрам, а потом помещаются в локальные переменные.
(В А1 — признак "успешности".)
При отсутствии первого аргумента (при условии сохранения разделяющих
аргументы запятых) оператор Kill ничего не делает, а только раскладывает
остальные аргументы по локальным переменным текущей подпрограммы.
* * *
ТАБУЛОСТОПЫ — существующее с незапамятных времён средство для облегчения
печати таблиц, или каких либо данных в виде аккуратных колонок. У пишущих
машинок это были выдвигающиеся железочки, помечавшие начала полей, а кнопка
ТАБ перемещала каретку до следующей такой позиции.
Фокал имитирует этот механизм, поддерживая таблицу табулостопов. Установить
(и считать) которые можно как ф-ей FViz, так и с помощью спецкомментария:
: : : : : : : ;
Здесь каждое очередное двоеточие отмечает начало поля, а завершаться
такая строка должна либо символом ! указывающим позицию автопереноса, либо
символом ; указывающим на продолжение "стандартным" способом — через каждые
восемь (или указанное всё той же FViz) число позиций.
Табулостопы автоматически используются оператором Write и могут быть
использованы так же операторами Ask и Type: двоеточие в их списке ввода/вывода
указывает на переход к позиции очередного табулостопа.
Развитие идеи табулостопов предполагает сопоставить каждому такому полю
сначала порядковый номер (чтобы заполнять или читать поля в произвольном
порядке), а потом и имя плюс тип, что даст возможность например обращаться к
полям записи из реляционной базы данных (типа .dbf). Но потребует двойной
буферизации: такая запись должна быть считана из файла и/или записана туда
только сразу и целиком. (Что известно как "транзакция".)
* * *
Минимальные средства работы с символами, а значит и с составленными из них
текстовыми строками были в Фокале всегда: ф-я FCHR преобразует символы в числа
(и обратно), с которыми дальше можно делать что угодно, в т.ч. хранить в
обычных переменных и элементах массивов... Да, кое что с отдельными строками и
Предыдущая глава |
↓ Содержание ↓
↑ Свернуть ↑
| Следующая глава |