Страница произведения
Войти
Зарегистрироваться
Страница произведения

Дополнение к гл. 4 книжки о Фокале


Автор:
Жанр:
Изобретательство
Опубликован:
20.07.2022 — 20.07.2022
Читателей:
1
Аннотация:
Нет описания
Предыдущая глава  
↓ Содержание ↓
↑ Свернуть ↑
  Следующая глава
 
 

Было даже введено средство чистить этот пул (оператором Eraze с ключевым словом Пул), и добавлять туда строчку. (Write Пул) Правда, всё это понадобилось только один раз — в некоторой демонстрационной программе, вот это всё и демонстрирующей.

0. Да, кстати, примем решение, что в именах переменных все буквы различаются, а в ключевых словах и именах встроенных функций — нет. То есть например буквы "W", "w", "В", "в" в имени переменной — разные, а в имени функции или будучи первой буквой ключевого слова — одна и та же.

Эквивалентность русских и латинских в основном очевидна: А-A, Б-B, Ц-C,... Но Q-Я, V-Ж, X-Ь, Y-Ы. Буква Э эквивалентна символу "", а Ю — "_" которые тоже буквы. И вообще, буква — всё что не цифра и не разделитель.

5.3.0 Красивости типа доопределения функций опираются на то, что у функции теперь может быть переменное количество аргументов. Причем аргумент может быть не только числом, но и текстовой константой. А так же может отсутствовать — просто пустое место между разделяющими аргументы запятыми. Лишние аргументы, если есть, игнорируются.

Здесь я ничего не придумал — в базовом Фокале всё это уже было — и переменное количество аргументов и их пропуск и текстовая константа в виде строки в кавычках. Правда по-отдельности. А нам это позволило разнообразить функционал.

5.3.1 Так, одна единственная функция FTMP делает абсолютно всё, что вообще надо делать с датой и временем. Без аргументов она выдаёт текущую дату в виде количества суток с начала тысячелетия. Его дробная часть (доли суток) это время. Используемого в интерпретаторе плавающего числа с двойной точностью (примерно 16 верных десятичных цифр) в принципе хватит для указания моментов времени с точностью до миллисекунды в пределах всей истории человечества, начиная с нижнего палеолита. Такое представление удобно чтобы с ним работать. (Например вычислить временной интервал между двумя такими моментами.) Но совершенно неудобно для восприятия.

Поэтому сама же функция FTMP с числовым аргументом его и расшифровывает: если он один — выдаёт в канал вывода в виде текста. Или выделяет и возвращает указанную вторым аргументом компоненту: 0 — секунды, 1 — минуты, 2 — часы, 3 — день месяца, 4 — месяц 5 — год. (Все целые, кроме секунд.)

Если первый аргумент пропущен — FTMP "собирает" дату/время из остальных.

А если первый аргумент — текстовая константа — FTMP пытается выделить из неё дату/время, в том числе по шаблону, указанному вторым аргументом. А без него — по: "ДД.ММ.ГГ чч:мм:сс". (Его же FTMP использует для преобразования даты/времени в текст.)

5.3.2 Вторая функция, работающая с временными интервалами — FCLK тоже немного усовершенствована: выдаёт интервал (от предыдущего "момента") не в "тиках" таймера, а сразу в секундах. (А сколько секунд в сутках — каждый, если что, и сам догадается: 60*60*24.) Этот самый "момент" она устанавливает текущим, только если без аргумента. Проще говоря — сбрасывает счетчик. Но может установить его равным первому аргументу. В том числе, если он отрицательный, то как бы переместив его в будущее.

В этом случае можно заказать "событие" — когда этот момент наступит. (Номер события — второй аргумент.) Или напротив отменить его (если первый аргумент отсутствует). Или вообще отменить все события — если отсутствуют два аргумента, но есть разделяющая их запятая: FCLK(,). Или же событие может происходить периодически — с интервалом, указанным третьим аргументом.

Ну а если второй аргумент таки есть, но отсутствует — производится тупое ожидание отнесенного в будущее "момента" — полный аналог имеющихся в других языках функций задержки SLIP. Например FCLK(-2.7,) подвесит машину на 2.7 сек.

3.0 "Событие" и "ситуация" — это происшествия, на которые программе надо как-то реагировать: как только оно произошло — вызывается заранее назначенная подпрограмма "реакции" и выполняет по этому поводу какие-то действия. Назначает реакцию и на события и на ситуации оператор Break. (И отменяет тоже.) А искусственно они порождаются: события — оператором Kill, а ситуации — оператором Quit. И тот и другой (кроме номера события или ситуации) могут содержать дополнительные аргументы, которые передаются в качестве параметров в подпрограмму реакции — становятся начальными значениями её локальных переменных. В точности так же, как при вызове подпрограммы спецфункцией FSUBroutine: там первый аргумент — адрес подпрограммы, а остальные... (Да и в операторе Do, вызывающем подпрограмму как процедуру, тоже теперь так сделано.)

Различаются события и ситуации вот чем:

Событие — глобальное. Оно происходит где-то во внешнем мире, и реакция на него это аналог прерывания. Собственно ДОС`овскими прерываниями события с номерами 1..255 и являются. А номера внутренних событий Фокала начинаются с 1000. Внутренних это типа нажатия кнопки на клавиатуре, или исчерпание очереди звуков, назначенных на воспроизведение с помощью встроенного динамика. Или вот — истечение временного интервала, назначенного функцией FCLK. (Хотя вот она то как раз может назначить номер события какой ей вздумается.)

А вот ситуация — локальная. В том смысле, что она возникает где-то в конкретном месте выполняющейся программы. Все "естественные" ситуации — это ошибки. А искусственные, порожденные оператором Quit, это средство прекратить её выполнение. Но если раньше выполнение программы всегда прекращалось полностью и управление получал пользователь, сидящий за терминалом, то сейчас управление получит подпрограмма, предусмотрительно поставившая (с помощью оператора Break) "ловушку" на данную ситуацию. (Так что механизм ситуаций это еще и средство передачи управления, известное как "структурный переход".)

Подпрограмма реакции на событие выполняется "вне очереди" — между операторами основной. Причем в ней самой подобная же реакция на другие события запрещена. (А вот если она передаст управление еще какой либо подпрограмме — там можно.) И как только она завершится — основная программа продолжится со следующего (очередного) оператора. Ничего не заметив.

Каждому отслеживаемому событию сопоставляется еще и счетчик. Потому что реакция на событие может быть запущена только когда работает "исполнительная" часть интерпретатора. А когда "интерфейсная" часть общается с пользователем (на предмет получения от него очередной командной строки) — нет. А вот подсчет событий, на которые заказана реакция, ведётся всегда.

Кроме того, некоторые операторы могут выполняться ну очень долго. Например вышеописанное тупое ожидание с помощью всё той же FCLK.

Этот счетчик передаётся подпрограмме реакции первым аргументом, а сам сбрасывается.

И вот еще: если "событием" является прерывание процессора, то Фокал его не перехватывает (т.к. в силу собственной неторопливости не успеет обработать), а только фиксирует — наращивая вот этот самый счетчик. А вот учиняет (с помощью функции FCLk например) — вполне без дураков.

С ситуациями всё обстоит по-другому: после возникновения, ситуация "распространяется": происходит рекурсивный выход из всех вложенных вызовов подпрограмм. До тех пор, пока управление не получит "интерфейсная" часть интерпретатора. После чего она "ругается" — выдаёт сообщение об ошибке.

Или пока у одной из подпрограмм не обнаружится подходящая ловушка. Тогда распространение ситуации прекращается; управление получает указанная в ловушке подпрограмма. После этого ошибка считается исправленной — установившая ловушку подпрограмма нормально завершается.

Поэтому ловушка на событие — глобальная: если указать другую реакцию на данное событие — она отменяет предыдущую. А "нулевая" реакция — вообще отменяет отслеживание данного события. (События — ресурс дефицитный: количество ячеек в таблице отслеживаемых событий — в данной версии всего 16 штук.)

А вот ловушка на ситуацию — локальная: она, вместе с локальными переменными — личное имущество поставившей её подпрограммы. Точнее: и то и другое размещается в кадре стека возвратов, соответствующего этой подпрограмме. При вызове подпрограммы, на этот стек добавляется новый кадр (в основном для сохранения там адреса возврата), а при возврате из неё — удаляется. И вместе с ним — всё хранящееся в нём вот такое локальное "имущество". Соответственно, при возникновении ситуации, все эти кадры последовательно удаляются со стека начиная с самого последнего, пока в одном из них не найдётся подходящая ловушка...

2.0 Кроме ловушек на ситуации, в кадре стека возвратов могут быть еще и локальные переменные, которые теперь — настоящие. Они — идейные потомки спецпеременной, & через которую функция FSBR передавала параметр в подпрограмму, носят имена &1, &2, ... &N где N — любое число. (Или $1, $2... чтобы как в UNIX`овском sh.) А & превратилась в &0 (она же $ или $0). Используются они для того же самого — передачи параметров в подпрограмму. Только теперь у той же спецфункции FSBR может быть не два аргумента, а сколько угодно. Да и оператор Do теперь это умеет... (Догадался сделать только когда написал вот это. Инерция мышления, однако.)

При завершении подпрограммы, локальные переменные вместе с её кадром стека ясный пень — удаляются. И становятся доступны предыдущие — в следующем кадре. Впрочем, они и так были доступны: текущие просто их "экранировали". Но если в данном кадре искомой переменной нет — она ищется в предыдущем, предпредыдущем и.т.д. А если так и не найдена — это не считается ошибкой (как для глобальных переменных): просто возвращается ноль.

6.1 Для третьего Фокала было задумано, что самыми глобальными из локальных переменных будут параметры в командной строке, указанные при запуске Фокала. Но они — строчного типа, а мы пока работаем только с числами.

В принципе до них тоже можно добраться, равно как и до "окружения" (известного как "environ") — набора строчек вида ИМЯ=значение, через которые UNIX`овский sh передаёт запущенной им программе значение некоторых своих макропеременных (тех, которые помечены как "экспортируемые"). Да и другие запускальщики программ как правило тоже. Но сделано это, прямо скажем — по дурацки: повешено на функцию FTEll, на которую свалили всё, что не знали кому поручить. А надо бы для этого ввести отдельное средство, что ни будь типа спецфункции FENviron... Но не будем забегать вперёд.

2.0.1* Таким образом "цепочка контекстов" (если про комплект локальных переменных можно сказать что это "контекст") не статическая, как в других языках ("структурных" и компилируемых, как например Паскаль), а динамическая.

Ну так там у них в Паскале (и других подобных языках) статическая и динамическая цепочки контекстов существенно конфликтуют. Щас объясню как.

В этом самом Паскале описания подпрограмм вкладываются друг в дружку как матрешки. Например внутри подпрограммы A могут быть подпрограммы B и C, а внутри B еще и D, E, F... И для всех для них локальные переменные подпрограммы A (например a1, a2, a3) находятся снаружи. И следовательно должны быть доступны во всех перечисленных подпрограммах, если конешно у них нету своих переменных с такими же именами. Ага. А в подпрограммах D, E, F еще и локальные переменные подпрограммы B (например b1, b2, b3). А вот те, что объявлены в подпрограмме C (например c1, c2, c3) — разумеется нет.

У нас — интерпретатор. У нас переменная ищется в тот момент, когда к ней идёт обращение, то есть во время выполнения программы. Вот в данном случае путем просмотра списков локальных переменных во всех кадрах стека возвратов, начиная с последнего. Да, это долго и дорого, но такова селяви. А у них — компилятор. Он заранее долго-долго что-то химичит с текстом программы, но всё это ради того, чтобы во время выполнения доступ к переменной производился в "одно касание" — единственной машинной командой, сразу лезущей по адресу, где она лежит. С локальными переменными всё просто: используется указатель на текущий кадр стека. (Чаще отдельный, но бывает что и указатель его вершины.) Адрес переменной — это смещение относительно его. А вот как быть если нам в подпрограмме B надо обратиться к переменной a2? Просто относительно того же указателя, но к предыдущему кадру, даже точно зная их размеры — не прокатит! Потому что перед этим подпрограммы B и C могли сорок раз успеть вызвать друг дружку рекурсивно, и вот на сорок первый раз вдруг понадобилось... То есть между кадром подпрограммы A, где вожделенная переменная a2 и текущим кадром активации подпрограммы B, на который смотрит оный указатель, еще сорок кадров активации подпрограмм B и C, пусть даже известного, но (из вредности) разного размера, а главное наваленных неизвестно в каком порядке. (Который, например, от содержания обрабатываемых ими данных, зависит!) Ну и как её искать? Тоже интерпретацией — последовательно просматривая все кадры, пока не найдется нужный... (Благо, если указатель кадра — отдельный, то его предыдущее значение сохраняется в текущем кадре, вместе со значениями других регистров, используемых данной подпрограммой. И все кадры получаются связанными в цепочку. Но это "динамическая" цепочка, как и у нас.) А как же тогда эффективность?!

Или, чтобы малость облегчить этот процесс, организовать еще и статическую цепочку. (Честно говоря, непонятно как.) Или поступить как в некоторых машинах Лебедева (отличающихся от машин фон-Неймана высокоуровневой архитектурой), например в Эльбрусе: завести не один, а целый массив указателей кадра, с номерами, соответствующими статической вложенности подпрограмм. Соответственно для обращения к локальным переменным используется пара чисел: номер такого регистра и смещение переменной относительно его. (Каждая подпрограмма, зная свой уровень статической вложенности, получив управление, сразу помещает адрес своего кадра стека в регистр с соответствующим номером. Старое его значение сохраняет в своём кадре. А при завершении — вертает всё в зад.) Но там всё это делается аппаратно, а тут — интерпретацией. И всё ради чего? Ради переменных с промежуточными уровнями локальности, которые возможно никому сто лет не понадобятся. В общем не было у бабы заботы — так купила себе порося!

Вон в Си этой надуманной академическими недоумками проблемы просто нет! А всего лишь не можно вкладывать подпрограммы друг в друга. Блоки (которые "составной оператор"), в начале которых могут быть в том числе и объявления переменных, расширяющих, кстати, текущий кадр стека — это пожалуйста. А вот подпрограммы — нет. А если надо куда ни будь упрятать вспомогательные функции (да и переменные тоже) чтобы не маячили и глаза не мозолили — для этого есть уровень файла с исходным текстом: пометь эти вполне себе глобальные объекты как "static" (на ключевом слове ребята явно сэкономили), и из подпрограмм, помещенных в другие файлы, они видны уже не будут. (А то слов мало, а сущностей много: вдруг в другом механизме вспомогательные детали захочется назвать также.) То есть всего два уровня "локализации": внутри подпрограммы и внутри модуля (файла с исходными текстами) и технологическая потребность удовлетворена, а проблема — не создана.

А у нас и того нет: имена локальных переменных — предопределенные.

1234 ... 678
Предыдущая глава  
↓ Содержание ↓
↑ Свернуть ↑
  Следующая глава



Иные расы и виды существ 11 списков
Ангелы (Произведений: 91)
Оборотни (Произведений: 181)
Орки, гоблины, гномы, назгулы, тролли (Произведений: 41)
Эльфы, эльфы-полукровки, дроу (Произведений: 230)
Привидения, призраки, полтергейсты, духи (Произведений: 74)
Боги, полубоги, божественные сущности (Произведений: 165)
Вампиры (Произведений: 241)
Демоны (Произведений: 265)
Драконы (Произведений: 164)
Особенная раса, вид (созданные автором) (Произведений: 122)
Редкие расы (но не авторские) (Произведений: 107)
Профессии, занятия, стили жизни 8 списков
Внутренний мир человека. Мысли и жизнь 4 списка
Миры фэнтези и фантастики: каноны, апокрифы, смешение жанров 7 списков
О взаимоотношениях 7 списков
Герои 13 списков
Земля 6 списков
Альтернативная история (Произведений: 213)
Аномальные зоны (Произведений: 73)
Городские истории (Произведений: 306)
Исторические фантазии (Произведений: 98)
Постапокалиптика (Произведений: 104)
Стилизации и этнические мотивы (Произведений: 130)
Попадалово 5 списков
Противостояние 9 списков
О чувствах 3 списка
Следующее поколение 4 списка
Детское фэнтези (Произведений: 39)
Для самых маленьких (Произведений: 34)
О животных (Произведений: 48)
Поучительные сказки, притчи (Произведений: 82)
Закрыть
Закрыть
Закрыть
↑ Вверх