↓ Содержание ↓
↑ Свернуть ↑
| Следующая глава |
Краткое, но почти полное описание Фокала (можно сказать — "ликбез").
Сначала "базового", потом — текущая версия "вторая базовая".
(ред 1.07 от 1.4.24) (ревизия.3 от 9.4.24 )
А то книжка моя "Фокал снаружи и изнутри" это конечно хорошо, но уж слишком
много текста. Да и писана чтобы понять что и как нужно БУДЕТ сделать, и почему.
А надо — что уже сделано, типа руководство и справочник. Да, есть и такое:
результат ревизии. Но что-то мне не нравится. Как-то там всё не так...
Поэтому пишем заново.
================
* * *
===============
Итак: Фокал — интерпретируемый операторный язык еще первого поколения,
заточенный под режим диалога и ориентированный на экономию всех видов ресурсов
(как ЭВМ, так и человека), поэтому, в частности, максимально лаконичный.
(И, кстати, "строчно-ориентированный".)
(Все лишние рассуждения, например про поколения языков программирования и
"операторность" — удалил к коврюжьей матери! Но таки надо указать на
"калькуляторную" сущность данного языка, само название которого: КАЛькулятор
ФОрмул в пику ФорТРАНу, который — транслятор. И что Фокал обычно сравнивают с
Бейсиком: да, действительно во многом похожие языки, но Фокал компактнее,
лаконичнее, гораздо лучше продуман и вообще предназначен совсем для другого.)
"ДИАЛОГ" заключается в том, что человек сидит за терминалом (каковым может
быть даже телетайп середины прошлого века) и вводит командные строки: Фокал
выдаёт "приглашение", в качестве которого обычно символ * (звёздочка); человек
набирает командную строку и нажимает "ввод" (ВК, Enter); Фокал берёт эту
строку и либо сразу выполняет, либо, если в начале строки есть номер, помещает
её под этим номером в память, чтобы выполнить потом. В процессе выполнения
делает то, что предписывают составляющие эту командную строку "операторы".
После чего выдаёт следующее "приглашение"...
"ОПЕРАТОР" предписывает одно элементарное (для данного языка) законченное
действие. Чаще всего: вычислить выражение и как-то применить его результат.
Оператор Фокала обязательно начинается с ключевого слова, указывающего что
надо сделать, и может содержать что-то еще: второе ключевое слово, выражение,
или даже несколько, тогда они разделяются запятыми, или больше ничего. Сами
операторы, если в строке их несколько, разделяются символами ";". Ключевые
слова подобраны на разные буквы алфавита и могут сокращаться до одной первой
буквы. (Остальные всё равно игнорируются.)
Примеры операторов Фокала и для сравнения аналогичных операторов Бейсика:
Type (2+2)*2 — напечатать результат PRINT (2+2)*2
Set X=(2+2)*2 — сохранить в переменной X LET X=(2+2)*2
Ask X — ввести в X число с терминала INPUT X
Coment любой текст — комментарий до конца строки REM комментарий
Фокал, как и современный ему Бейсик, работал с одним единственным типом
данных — числами с плавающей запятой. Операций с ними — пять: + — * / ^.
(Последняя — возведение в степень.) Все скобки ([{<>}]) эквивалентны. Все
кавычки " ' ` — тоже. В кавычках — текстовые константы. Они используются в
операторах ввода и вывода Ask и Type для выдачи пояснений ко вводимым и
выводимым числам. Если там нужна кавычка — заключаем текст в кавычки другого
типа.
Обратим внимание: в операторах ввода/вывода Ask и Type допустим, не один, а
произвольное количество элементов списка ввода/вывода. Кроме текстовых констант
в виде символов в кавычках, а так же ! (восклицательного знака) предписывающего
переход на следующую строку, он включает для Ask — переменные, куда помещать
вводимые числа, а для Type — выражения, значения которых вывести. А так же
таинственную конструкцию "формат", указывающую как их выводить (см. далее).
ПЕРЕМЕННЫЕ — только глобальные. И тоже, как и в Бейсике, не требуют
предварительного объявления, а создаются по мере надобности — в момент
первого присваивания. То же самое относится и к элементам массивов, которые
на самом деле — переменные с индексами: каждая из них существует индивидуально.
(Тогда как в Бейсике массив таки надо предварительно объявить оператором DIM.)
Это несколько расточительно, зато удобно. И сама собой исчезает проблема
начального индекса: с единицы, как в Бейсике, нумеруются элементы массива, или
с нуля как в Си? Но в качестве побочного эффекта — возможность обратиться к
одной и той же переменной с разным количеством индексов, да еще и зависящая от
реализации. Впрочем, обычно гарантируется, что нулевое значение индекса
эквивалентно его отсутствию: и Х(0,0) и Х(0) это то же самое, что просто Х.
Надеюсь, уже не надо объяснять, что "переменная" это такая штучка, в которой
можно хранить одно число?...
ИМЕНА ПЕРЕМЕННЫХ распознаются по двум первым буквам. Для калькулятора
этого более чем достаточно. Остальные если есть — игнорируются. Как и в других
языках, имена могут состоять из букв и цифр и начинаться должны обязательно с
буквы. Но буквой в Фокале считается всё, что не цифра и не разделитель. Т.е.
это не только русские буквы, но и всякие значки типа @ # $ & | _.
В отличии от переменных, имена всех встроенных функций начинаются на букву
Ф (а переменные на букву Ф называть нельзя, увы) и распознаются по первым
уникальным буквам. При чём, чтобы не гадать как правильно: FRAnd или FRNd,
реализованы все альтернативные варианты. Но именованные функции — только
встроенные. Возможности назначить имена подпрограммам не предусматривается.
Однако, предусматривается возможность подгрузки недостающих библиотечных
функций: оператором Load (до сих пор не реализованная).
Набор встроенных функций, как и у Бейсика, включает математические (корень,
логарифм, экспонента, прямые и обратные тригонометрические), генератор
случайных чисел, абсолютное значение числа а так же средства получения его
целой и дробной части. Но вместо SLEEP тупо приостанавливающей выполнение
программы — FCLk сообщающая длительность временного интервала от некоторого
момента, ею же в прошлый раз и установленного. В дополнение к работающим с
числами Ask и Type, функция FCHr для побайтного ввода/вывода. И функция FX для
управления оборудованием, в том числе нестандартным.
Особняком стоит функция FSUbr предназначенная для обращения к подпрограмме
как к функции — из середины выражения. С передачей параметра и возвратом
результата. (Можно даже сказать, что это функциональный аналог оператора Do.)
Параметр попадает в спецпеременную с именем &, а в качестве результата
возвращается значение последнего вычисленного в подпрограмме выражения. (Не
важно в каком операторе.)
В Бейсике, кстати, было сделано аналогично. Там это называлось "функция
определяемая пользователем". Но там она была одна единственная и требовала
предварительного объявления. (Что, кстати, вместе с операторами DATA/READ,
сохранившимися и поныне, указывает на то, что это сляпанное на скорую руку
подмножество всё того же Фортрана было таки изначально ориентировано на
компиляцию, причем с перфокарт.)
ПРОГРАММНЫЕ СТРОКИ в Фокале, как и в Бейсике — нумеруются. Номера строк
служат как метками для передачи управления, так и средством их упорядочивания:
хранятся и выполняются они в порядке нумерации, независимо от того, в каком
порядке введены. Новая строка с тем же самым номером замещает уже существующую.
Строка без номера называется "прямой" (или "нулевой") и выполняется сразу.
(А нумерованные — соответственно "косвенными".)
Но в отличии от Бейсика, номера строк не целые числа, а дробные.
В результате программа распадается на структурные единицы — "группы" строк.
Так, что целое число обозначает всю такую группу, а дробное — конкретную
строку в ней. А когда нужна вся программа (например в операторе Write, Modify,
или Eraze (см. далее)) — пишется ключевое слово Ales, что значит "всё".
Каждая такая группа это "естественная подпрограмма", где сам бог велел
разместить одно законченное сложное действие. При передаче управления такой
структурной единице оператором обращения к подпрограмме Do или функцией FSBr,
возврат автоматически происходит по достижении её конца. При чем даже в том
случае, если там нет оператора возврата Ret.
Каждая отдельная строка — это тоже естественная подпрограмма! Что удобно
например для отладки: можно запускать строки по одной и смотреть что
получается... Но запустить подпрограмму с "дополнительной точки входа" (т.е.
не с начала группы) — невозможно.
Как правило может быть до 99 групп и до 99 строк в каждой группе. Но на
самом деле — на сколько памяти хватит. Однако, обратим внимание: номер группы
и строки в ней это не два целых числа, а одно дробное. Так что строка с
номером 1.1 это то же самое, что 1.10, а вовсе не 1.01!
Операторы УПРАВЛЕНИЯ порядком действий:
Go N_строки — безусловный переход к строке с указанным номером
Do N_строки — переход к подпрограмме
Ret — возврат из подпрограммы
If (Усл) N1,N2,N3 — условный переход
For i=Нач,Кон,Шаг; ... — цикл со счетчиком
Quit — стоп (и в "прямой" строке — выход из Фокала)
Условный оператор сравнивает значение "Усл" с нулём — поэтому в нём три
адреса: для случаев когда оно меньше нуля, равно и больше. Последние из них,
впрочем, могут быть опущены и тогда в соответствующих случаях выполняется
остаток строки. Обратим внимание: условие обязательно в скобках.
В операторе For циклически выполняется остаток строки после оператора. И он
тоже — "естественная подпрограмма": куда бы мы оттуда ни передали управление,
оно всё равно вернется заголовку цикла. Но досрочно выйти из цикла (как в
других языках — просто передачей управления за его пределы) — невозможно. Для
этого придётся установить значение параметра цикла больше конечного.
(Разумеется, в качестве параметра цикла можно использовать любую переменную,
включая элементы массива.) Выражения в заголовке цикла вычисляются только один
раз — перед первой итерацией. И последние из них, так же как и в операторе If,
могут быть опущены. Тогда шаг будет +1. А без конечного значения оператор
эквивалентен присваиванию.
Обратим внимание: оператор цикла (в отличии от аналогичного оператора
Бейсика) можно использовать и в ненумерованной, "прямой" строке.
Необходимо отметить, что в Фокале выражение произвольного вида может быть
везде, где по смыслу требуется число. В том числе и в качестве адреса в
операторах перехода — будет "вычисляемый переход", аналог оператора switch
языка Си. И даже когда ввода ожидает оператор Ask. (Который фактически левая
часть оператора присваивания. А правую вводит сидящий за терминалом...)
Константа — только в конструкции "ФОРМАТ" оператора Type: два целых числа
через точку после символа % (процент) — указывающих сколько всего позиций под
число и какова точность. (Одиночный % восстанавливает формат "по-умолчанию".)
Например: известно, что facos(-1) это число Пи. С разными форматами:
номера позиций: 123456789_123456789_123456
T %5.5 facos(-1),-2 -> 3.1416 -2
T %10.5 facos(-1),-2 -> 3.1416 -2
T %5.10 facos(-1),-2 -> 3.141592654 -2
T %1.10 facos(-1),-2 -> 3.141592654 -2
T %1.40 facos(-1),-2 -> 3.14159265358979312 -2
T % facos(-1),-2 -> 3.14159 -2
Таким образом, рассматриваемая версия (использующая плавающее число
"двойной точности") даёт максимум 18 значащих цифр и действительно выделяет
под число поле указанной ширины. Но если число занимает больше позиций — без
зазрения совести вылезает за пределы этого поля. Да еще и добавляет после числа
пробел (чтобы идущие подряд числа не сливались), от которого решительно
невозможно (было) избавиться! Но сейчас — можно: он подавляется при наличии
текстовой константы. В том числе и пустой:
T facos(-1),-2 -> 3.14159 -2
T facos(-1),"",-2 -> 3.14159-2
И наконец по желанию па-ачтеннейшей публики... (Ну да, забыл я про это.)
T %010.5 facos(-1),-1 -> 00003.1416 -000000001
"ОБСЛУЖИВАНИЕ" ПРОГРАММЫ, в отличии от Бейсика, производится тоже
операторами языка:
Write n — вывести на терминал строку или группу строк номер n
Eraze n — стереть -//-
Modify n — редактировать строку n (а не вводить целиком заново)
Write — вывести всю программу
Write Ales — -//— плюс заголовок-комментарий
Eraze Ales — стереть всё — привести интерпретатор в исходное состояние
Eraze — стереть только переменные (программу — нет)
Запустить выполнение находящихся в памяти строк — любым оператором передачи
управления: Go, Do и даже If. Но если с самого начала и всю программу — то Go
или Do с ключевым словом Ales, или вообще без аргументов.
Распечатать на бумаге или сохранить программу на внешнем носителе — тоже
оператором Write. Но для этого сначала надо переключить (оператором Operate)
канал вывода на одно из подключенных к машине устройств (например на принтер:
O L), а потом обратно на терминал: O L; W A; O T.
Загрузить ранее сохраненную программу еще проще: достаточно переключить на
неё канал ввода, и интерпретатор прочитает её оттуда сам. Но когда она там
кончится, произойдёт ошибка "конец носителя". А по любой ошибке каналы ввода
и вывода автоматически устанавливаются обратно на терминал и его клавиатуру.
(Но вполне можно было добавить в конце какую ни будь прямую команду: хоть
переключающую канал ввода обратно на клавиатуру принудительно, хоть сразу же
запускающую только что загруженную программу: O P;W A;T "O K"!;O T)
В оные времена внешних устройств у машины было немного и они указывались в
операторе Operate вторым ключевым словом: Lpt — принтер, Prf — перфоратор,
Rd — перфосчитыватель, Tty — терминал, Kbd — его клавиатура. А сейчас второе
ключевое слово превратилось в "псевдоним" открытого файла. Впрочем L всё так
же обозначает превентивно открытый в ДОС`е файл стандартного принтера, а T и K
терминал и клавиатуру.
ЗА КАДРОМ ОСТАЛСЯ оператор Xecut (надо бы execut — "выполнить", да буква Е
уже занята), просто вычисляющий выражение и игнорирующий его результат. Он
нужен для вызовов функций с "побочным эффектом" (например FCHr(x) выдающую
байт с кодом х в канал вывода) когда нужен этот эффект, а возвращаемое
функцией значение — нет. Оператор Kill сбрасывающий все внешние устройства в
исходное состояние. (Сейчас после этого пришлось бы перезагружать компьютер.)
И оператор "?" (вопросительный знак), который будучи поставлен перед любым
другим оператором, включает (или выключает) режим "трассировки" — когда текст
↓ Содержание ↓
↑ Свернуть ↑
| Следующая глава |