Предыдущая глава |
↓ Содержание ↓
↑ Свернуть ↑
| Следующая глава |
У нас для этого есть редактор командной строки (1.2) со всяческими прибамбасами, типа "кармана" под фрагмент строки, пула ранее введенных строк и возможности захватить фрагмент с экрана. Ну так Modify использует его напрямую, а например Ask — только когда он на том конце канала ввода.))
Но нам сейчас интересно что у этих каналов на другом конце.
В оные времена, когда Фокал работал на "голой" машине, у неё было всего
несколько устройств ввода/вывода, которые и указывались в операторе Операте
вторым ключевым словом: терминал (Tty) и его клавиатура (Kbd); принтер (Lpt);
так называемая "перфостанция" — ленточный перфоратор (Prf) и пеофосчитыватель
(Rd). И всё.
Долговременное хранение информации (и программ и данных) — на перфоленте.
Главное — каждое из этих устройств работало либо только на ввод, либо
только на вывод. Поэтому путаницы не возникало. И оператор О выглядел так:
Operate Устройство (Например: O L; — переключить вывод на принтер.)
Сохранить находящуюся в памяти программу (или вывести её на печать) — тем
же самым оператором Write, что и вывод её на терминал. Только сначала надо
переключить канал вывода туда, а потом — обратно: O P; W A; O T
Загрузить — еще проще: просто переключить канал ввода на считыватель: O R
(только сперва ленту туда поставить) и Фокал всё прочитает оттуда сам.
В точности так же, как и с терминала читает: ему без разницы. Но вот когда
ленточка кончится — произойдёт ошибка "конец носителя" и Фокал сам собою
переключит каналы ввода и вывода на терминал и клавиатуру. И заругается.
Поэтому изящнее (чтобы не ругался), когда последняя команда на ленте делает
это сама. Для этого выводим так: O P; W A; T "O K"!; O T
А можно было и "G" дописать — тогда бы только что загруженная программа
сразу бы и запустилась...
А вот теперь у нас вместо перфоленточки, на которой можно карандашом
написать всё что посчитаешь нужным, смотать и положить в коробочку, например
из под сухого "вафельного" торта, присланного тётушкой к очередному празднику
(размеры как раз подходящие) — то, что называется "файл". Впрочем, от
перфоленты качественно мало чем отличается: такая же в точности
последовательность байтов, разделенных на строчки символами конца строки.
(Которые мы в операторах Type восклицательными знаками обозначаем.) Разве что
перемотать можно. (Перфоленту — тоже можно, но вручную.) А еще можно писать
поверх того, что уже написано. (Вот это для перфоленты — увы...) Но сначала
надо "открыть", а потом — "закрыть". Ага. Но и перфоленту в считыватель тоже
сначала надо поставить, а потом... Но это же вручную! А тут операционная
система сделает всё это за нас: найдёт в виртуальной коробочке (по имени),
поставит в виртуальный считыватель... Только команды ей подавай.
Главное то, что там считыватель был один, а здесь таких вот "виртуальных
считывателей" — много. (Как минимум несколько.) Фиксированные ключевые слова
для их обозначения не годятся.
(1.1) Поэтому еще в незапамятные времена было принято решение, что во-первых
возложить все эти действия по открытию/закрытию (а потом и позиционированию)
на всё тот же оператор Oper. А во-вторых что второе ключевое слово в нём
теперь будет и не ключевым словом вовсе, а "псевдоним" открытого файла.
Соответственно открываем файл так: Ope "имя_файла" Псевдоним;
Тоесть меж первым и вторым ключевым словом — текстовая константа. Закрываем в
точности так же, только константа — пустая. А переключаем на него канал ввода
или вывода — как и раньше.
А оператор Write с ключевым словом Oper запрягли сообщать — какие именно
файлы на настоящий момент открыты. При чем сообщает он это так, чтобы
получились команды Фокала, которые как раз и откроют эти файлы, если не на
терминал выдавать, а где ни будь сохранить и следующий раз — выполнить. (1.6)
Но файл можно открыть как на ввод так и на вывод. Да и одновременно на то
и другое — тоже. Поэтому в том ключевом слове, которое "псевдоним",
игрорируются не все буквы после первой. Некоторые (R W и еще A) указывают как
именно открывать: на ввод, на вывод, или на добавление — тоже на вывод, но в
конец, добавляя к тому, что в файле уже было. А при переключении — какой из
каналов имеется в виду. Задействованы так же буковки B и T указывающие что файл
открывается как "бинарный" и как "текстовый" (по умолчанию).
Псевдонимы T K L остаются закреплёнными за терминалом, клавиатурой и
принтером. Добавляются еще I O E для стандартных ввода, вывода и вывода
ошибок. А псевдонимы А и Б резервируются для двух первых аргументов в командной
строке, которой запущен интерпретатор: первый — предположительно файл с
программой, а второй — файл под протокол. Поэтому, если есть, открываются на
ввод и добавление соответственно. И на А сразу же переключается канал ввода.
Если аргументов в командной строке нет — делается попытка найти файл "sav.f"
и тоже открыть под псевдонимом А...
(1.3) "Позиционирование": Oper (позиция) Псевдоним;
"Позиция" — смещение указателя ввода/вывода от начала файла в байтах.
Ноль — начало файла, -1 — конец. Скобки обязательны — по ним и.
А чтобы получить текущую позицию — пришлось добавить отдельную функцию,
которую по аналогии с Си-шной назвали FTEll. Но сразу возникла проблема: как
ей указать, позиция в каком из имеющихся в наличии открытых файлов нам нужна?
Указывать ей в качестве аргумента псевдоним из оператора Open было бы
нарушением принципов: опять некий специальный тип данных, который придётся
разрешить использовать везде... Нет уж! Поэтому (по аналогии с FSUbr — см.
далее) было решено, что это будет тот файл, который поучаствовал в последней по
времени операции ввода/вывода. В том числе и фиктивной, как O () X;.
(Позиционирование которое ничего никуда не позиционирует. Потому что в круглых
скобках ничего нет.) Забегая далеко вперёд (5.0) скажем, что вот так и
появился на свет "потоковый" аккумулятор А3...
А потом свежеизобретенную функцию FTEll загрузили дополнительной работой:
сперва сообщать не только позицию в файле, но и его размер — указав ей это с
помощью аргумента. Вернее самим фактом его наличия: свой аргумент, если он
есть, FTEll не просто никак не использует, но даже и не вычисляет!
А потом, с аргументом в виде текстовой константы, запрягли FTEll искать
файлы в текущем каталоге по указанному ею имени. Или "шаблону" — как это
принято в операционной системе, когда вопросительный знак (какового в имени
файла быть не может) заменяет собою один любой символ, а звёздочка — сколько
угодно. Но под шаблон могут подойти имена нескольких файлов. Найти следующий -
с аргументом в виде пустой строки. Если не нашла — вернёт -1.
Потом сообразили, что у терминала с клавиатурой никакой позиции и длины нет.
(Вернее конечно есть, но это позиция курсора на экране. И ею занимается другая
функция — FKурсор она же FCS.) И запрягли опрашивать буфер клавиатуры и
нажатость "интересных" кнопок (Ctrl, Alt, Shift).
Потом еще кое что придумали, но это лучше в хелпе посмотреть...
Кстати, пока не забыли: а не помешает ли нам "приглашение" в операторе Ask
в строке 5.1? В смысле — не удалить ли оттуда текст "введите А, Бэ и Цэ: ",
чтобы его не выдало нам куда ни будь сорок тысяч раз. (И, кстати — куда?)
А вот это — от реализации зависит: в базовом Фокале (1.0 и даже 1.1) лучше
бы удалить — во избежание. Но потом было сделано так, что оператор Type выдаёт
свои текстовые константы куда угодно (в смысле — пишет в канал вывода), а вот
оператор Ask — только и исключительно на терминал. И только в том случае, если
канал ввода указывает на клавиатуру. Так что ничего страшного.
Кстати, если этого самого "приглашения ко вводу" в виде текста в кавычках в
операторе Ask нету, то он выдаёт в качестве приглашения символ двоеточие.
Чтобы "подавить" это — надо таки написать: "" (в кавычках — пусто).
Аналогично, оператор Type после вывода очередного числа, добавляет пробел
(чтобы числа не сливались), от которого решительно невозможно избавиться.
Вернее это раньше было невозможно, а сейчас его вывод откладывается до вывода
следующего числа или до конца оператора. А текстовая константа (в том числе и
пустая) его отменяет.
А вот если пользователь ввёл строку, в которой несколько чисел, а оператору
Ask нужно только одно — остальные останутся до следующего раза. А следующий
оператор Ask всё равно тупо выдаст это самое приглашение ко вводу, не смотря на
то что числа для него уже есть и вводить ничего не нужно. Забегая вперёд (4.1)
Ask может очистить свой входной буфер (A %) и тем самым проигнорировав
оставшееся с прошлого раза — велеть вводить по-новой.
* * *
Итак, задачку-то мы решили, но использовали (и продемонстрировали) далеко
не все средства даже базового (1.0) Фокала. Поэтому произведём ревизию,
посмотрим — что осталось за кадром?
Кстати, у нас (1.2) есть оператор Help — так он без аргументов выдаёт
краткую справку (на один экран) — самое начало файла foc.hlp, где как раз всё
что есть в Фокале и перечислено.
Но мы перечислим по-новой:
— вычисления: Set — вычислить выражение и сохранить результат в переменной
Xecut — тоже вычислить выражение, а результат — проигнорировать
Надо бы "екзекут" — выполнить, да буква Е уже занята под более нужный оператор
Eraze. А Xecut — для выражений с "побочным эффектом" (это может быть например
звук, издаваемый с помощью функции FBip), когда нужен вот этот вот побочный
эффект, а возвращаемый функцией результат — нет. Но он тоже не пропадает, а
застревает в некотором "числовом аккумуляторе", он же А1.
— ввод/вывод: Ask — ввод
Type — вывод
Write — вывод текста программы (и другой полезной информации)
Operate — управление вводом/выводом (переключение каналов)
Modify — редактирование строк программы
— обслуживание программы: Go, Do — запуск на выполнение
Write — вывод
Modify — исправление
Eraze — удаление
Eraze команда опасная, поэтому если и Go, и Do, и Write и Modify без
аргументов действуют на всю программу (почти так же как с ключевым словом All)
то Eraze без аргументов удаляет только переменные, освобождая таким образом
память перед новым запуском программы, а всё остальное — не трогает.
— управление порядком выполнения программы: Quit — останов программы
If — условный переход
Go — безусловный переход
Do — вызов подпрограммы
Ret — возврат из подпрограммы
For — цикл со счетчиком
Цикл со счетчиком позволяет что-то сделать указанное (фиксированное) число
раз. Выглядит так: For сч = нач_зн, кон_зн, шаг ; остаток строки...
Где сч — любая переменная, в т.ч. можно и с индексом. Далее — три выражения.
(Или меньше.) Они вычисляются один раз — в самом начале. Начальное значение
сразу присваивается переменной-счетчику. Далее многократно выполняется остаток
строки. (Кстати — как подпрограмма!) А значение переменной-счетчика каждый раз
увеличивается на величину шага. До тех пор пока не станет больше конечного
значения. (С (1.5) решено, что больше чем на пол шага.) Но после цикла
остаётся то, которое было на последней его итерации.
Например (шутка): написать "ку-ку" столько раз, сколько укажут времени
Ask "который час?",N; For i=1,N; Type "ку-ку"!
Если остатка строки под тело цикла мало — его вполне можно вынести в
отдельную группу, написав Do N_группы. Или, если столько места под него
ненадо — продолжить в другой строке, написав Go N_строки;. А вот завершить
таким методом выполнение цикла, просто выйдя за его пределы (как это делают в
других языках) — не получится: тело цикла — подпрограмма. И каждая
программная строка — тоже "естественная подпрограмма". Куда бы мы ни передали
из цикла управление, по завершению строки управление вернётся заголовку цикла.
Поэтому чтобы досрочно из него выйти — следует установить значение счетчика
цикла больше конечного.
Но можно так же остановить программу оператором Quit. Или выполнить т.н.
"структурный переход", породив тем же самым Quit "ситуацию". Но это уже (3).
— другие операторы: Coment — коментарий
Load — подгрузка спецфункций (не реализован)
Kill — ("убить") — сброс (не реализован)
Prepare — распределение памяти (не реализован)
Break — реакция на прерывания (начиная с (3))
? — трассировка и ?? — пошаговый режим
(примечание: отделять ? или ?? от следующего оператора ; необязательно)
Трассировка заключается в том, что текст выполняемых операторов выдаётся на
терминал — чтобы отслеживать по какому пути идёт выполнение программы.
В пошаговом режиме очередной оператор выполняется только после нажатия кнопки
на терминале. Так можно сделать еще один шаг, (в т.ч. как с заходом в
подпрограммы так и без), остановить программу, или продолжить — как с
трассировкой так и без.
В данной реализации из пошагового режима можно так же ввести "прямую"
командную строку Фокала.
Ну вот: для (1.0) операторы — все.
А мы ничего не забыли?
Как запустить программу на выполнение — понятно: любым оператором
передачи управления: Go, Do и даже If.
А остановить как?! (Если сама не останавливается...)
Организовать какую либо ошибку.
— если ввода ждёт оператор Ask — ввести ему некорректное выражение, например
содержащее несуществующую переменную
— если не ждёт, то нажать любые две кнопки: буфер клавиатуры всего на один
символ — будет его переполнение.
— сейчас (начиная с (1.2)) можно еще нажать комбинацию кнопок Cntrl/Ц
(А то например вот такую программку: 1.1 Type fchr(-1)!; Go 1.1
сообщающую коды нажатых кнопок, по другому не остановишь: функция FCHr берет
эти коды из буфера клавиатуры всяко быстрее чем мы нажимаем эти кнопки!)
Ну да, еще — встроенные функции:
FSQrt FExp FLOg — корень, экспонента, логарифм
FSIn FCOS FTAn — тригонометрические прямые
FASin FACos FATan — -//— обратные
FABs FItr FMOd FSGn — модуль, целая и дробная часть числа, знак
FCLk FRnd — таймер, генератор случайных чисел
FCHr FX — посимвольный ввод/вывод, обращение к внешним устройствам
FSUbr — обращение к подпрограмме как к функции (а не к процедуре — как Do)
Вот это и есть весь "базовый" Фокал.
* * *
(1.9) а так же (1.8), (1.7) и (1.4)
То, что на этапе (1.4) сначала напридумывали еще функций:
FTMp FBip FKурсор FЦвет FMышь — дата/время, звук, курсор, цвет, мышь
и сразу же (хотя у меня и написано, что это (1.7)) взялись расширять их
Предыдущая глава |
↓ Содержание ↓
↑ Свернуть ↑
| Следующая глава |