Руководство по ОС Excelsior - Части 1-5

Скачать PDF


Содержание Части 1-5 Части 7-8 Части 9-11

Книга первая

ОС Excelsior ДЛЯ НАЧИНАЮЩИХ



Короткое вступление

Кронос, в древнегреческой мифологии титан, сын Урана (Неба) и Геи (Земли). Уран, боясь погибнуть от одного из своих детей, возвращал их снова в недра земли. Поэтому Гея, изнемогавшая от бремени, уговорила Кроноса, родившегося последним, оскопить Урана. Кронос стал верховным богом. Испугавшись предсказания матери, что он в свою очередь будет свергнут одним из сыновей, Кронос проглатывал детей, рожденных ему сестрой Реей, пока ей не удалось спрятать от Кроноса и вырастить втайне Зевса. Возмужав, Зевс заставил Кроноса изрыгнуть проглоченных им детей, составивших поколение олимпийских богов,а сам Кронос и другие титаны, побежденные Зевсом, были заключены в Тартар. По более позднему варианту мифа, Кронос впоследствии был переселен на "острова блаженных". Отсюда в представлении древних греков "царство Кроноса" соответствовало сказочному "золотому веку". Считаясь первоначально богом жатвы, Кронос изображался старцем с покрытой головой и серпом в руке.

Позднейшее представление о Кроносе, как о боге времени, возникло вследствие созвучия имени Кронос и греческого слова "хронос" - "время".

Из русских и советских энциклопедических изданий

Теперь, когда законное любопытство читателя удовлетворено, можно начинать рассказ о Кроносе и об операционной системе Excelsior, под управлением которой работает Кронос.

Эта книжка похожа на слоеный пирог. Каждый может выбрать для себя наиболее подходящий слой. Опытный программист, работавший на разных машинах и в разных системах, скорее всего, выберет слой, бегло знакомящий читателя с соглашениями, принятыми в ОС Excelsior.

Для другого программиста, возможно, ОС Excelsior станет первой системой, которая научит его работать, а эта книжка - первым путеводителем в науку программирования (computer science).

Многие, работая на Кроносе, ощущают его как живое существо. Как хорошо, когда это существо тебя понимает, и как нелегко - понять его. Наша цель - научиться разговаривать с Кроносом.



Часть 1. Диалог с Кроносом

Глава 1.1. Клавиатура и экран

Для общения с Кроносом необходимы две вещи: клавиатура (чтобы вы могли говорить) и экран (чтобы вы могли видеть его ответ).

1.1.1. Курсор

На экране почти всегда можно найти ярко светящийся прямоугольник. Это курсор. Он указывает ту позицию на экране, на которой вы сейчас "находитесь". Если теперь нажать на клавишу "А", на том месте, где был курсор, возникнет изображение символа "А", а сам курсор переместится на одну позицию вправо.

При нажатии некоторых клавиш никакие символы не появляются, а происходит что-нибудь другое - например, перемещается курсор. Это зависит от того, какую клавишу вы нажали - литерную или управляющую.

1.1.2. Литеры

Литерные клавиши - это те, при нажатии которых на экран выдается какой-нибудь печатный символ: ABcd @#$ !* пЮЪЙ.

Литеры бывают прописные (ABCАБВ) и строчные (abcабв), кириллица (АБВгде) и латиница (ABCdef). ОС Excelsior все их различает и понимает. В системе используются две кодировки символов. Для латиницы кодировка ASCII-8 (American Standard for Information Interchange), для кириллицы - КОИ-8. Кодировку каждой клавиши можно узнать с помощью утилиты ascii (см. в томе "Утилиты ОС Excelsior").

1.1.3. Управляющие клавиши

Управляющие клавиши - клавиши, при нажатии которых терминал отрабатывает какие-либо функции.

Терминалы (и клавиатуры) бывают разные, и на всех работает одна и та же система. Везде в дальнейшем описании мы будем пользоваться некоторыми условными названиями кнопок. Каждой такой кнопке соответствует какое-либо действие. Где они расположены на вашей клавиатуре, вы можете узнать у администратора системы.

1.1.4. Контрольные символы

Этот раздел можно прочитать потом.

Кроме основных функциональных клавиш, используются еще и контрольные символы - символы, полученные одновременным нажатием клавиши CTRL и литеры. Заметим, что при наборе контрольного символа на клавиатуре не происходит его высвечивания на экране. Кроме того, не имеет значения, набирать контрольный символ с прописной буквой или со строчной. Можно набирать его с включенным регистром РУС - результат будет одинаков. В дальнейшем будем обозначать контрольные символы CTRL_<ЛИТЕРА>.

Некоторые контрольные символы имеют особую интерпретацию. Среди них:

  • CTRL_C - прекращение текущей задачи;
  • CTRL_X - открепление текущей задачи;
  • CTRL_S - останавливает процесс вывода на терминал;
  • CTRL_Q - возобновляет вывод на терминал;
  • CTRL_Z - переключает режимы вставки/замены.

Такая интерпретация контрольных символов сохраняется при любой работе с терминалом - в частности, в редакторе текстов 'ex' (см. главу про редактор).



Глава 1.2. Вход в систему

- Так, значит, тут разыгрывается Настоящая Шахматная Партия?! И целый мир - шахматная доска. Если, конечно, это настоящий мир. Как здорово! Как бы я хотела туда попасть! И... и стать пешкой... если позволят! Хотя БОЛЬШЕ ВСЕГО НА СВЕТЕ я хотела бы стать Королевой.

Л.Кэрролл. Алиса в Зазеркалье

1.2.1. Приглашение к работе

Сядьте поудобней и посмотрите на экран. Если все работает, то вы увидите на экране приглашение. Например, такое:

17:55 usr

Не огорчайтесь, если приглашение выглядит совсем не так. Просто это не ваше приглашение. Чтобы приглашали именно вас, попросите администратора вашей машины зарегистрировать вас в системе и завести для вас рабочую директорию.

Как правило, вас регистрируют под каким-то (разумной длины) именем или иным милым вашему сердцу буквосочетанием. С этого момента система знает, что вы являетесь одним из пользователей, со всеми вытекающими отсюда последствиями. Вы имеете право войти в систему и получить доступ к определенной информации; в свою очередь, вы можете хранить свою информацию и определять степень ее доступности для остальных пользователей. Кроме того, вы можете создать себе свою, отличную от окружающей, обстановку и работать в ней. В частности, установить любое понравившееся вам приглашение.

1.2.2. Как войти в систему

- Уважаемый Гладиолус, как жаль, что ты не можешь сказать ни слова.

- Отчего же, могу, - вымолвил Гладиолус. - Могу, если рядом есть кто-нибудь, кто этого ЗАСЛУЖИВАЕТ.

Л.Кэрролл. Алиса в Зазеркалье

После того, как машину включили и произошла загрузка системы, на экране появляется надпись

username:

Наберите на клавиатуре то имя, под которым вас зарегистрировали в системе. Затем найдите главную кнопку, которая называется ENTER, а может быть, CR (Carriage Return), или ВК (Возврат Каретки) - зависит от клавиатуры, на которой вы работаете. Нажмите ее. После этого на экране появится слово

password:

Это значит, что вы должны набрать ваш пароль. Если имя или пароль набраны неверно (а исправления при наборе запрещены), вам предложат попытаться набрать пароль еще пару раз. Если вы снова ошибетесь (или имя было набрано неверно), то после некоторой задержки опять появится надпись

username:

и все начнется сначала.

Когда, наконец, имя и пароль набраны правильно, на экране должно появиться ваше приглашение. Если это произошло, то все в порядке: можно начинать знакомство.



Глава 1.3. Командная строка

Диалог с ОС Excelsior ведется с помощью командных строк. Командная строка - набор символов, завершающийся нажатием клавиши ENTER.

Ведущие пробелы в командной строке игнорируются; следующие за командной строкой пробелы отсекаются. Количество пробелов между словами несущественно.

Командная строка должна содержать только одну команду.

Набранную командную строку можно редактировать.

1.3.1. Редактирование командной строки

Самая важная клавиша - ENTER (Carriage Return – Возврат Каретки). Именно она завершает набор каждой командной строки. Неважно, в какой позиции строки при нажатии ENTER находился курсор - будет введена вся командная строка от начала до конца.

Ввод командной строки с "обрубанием" хвоста (то есть всего того, что находится правее курсора) осуществляет клавиша LF (Line Feed - Перевод Строки - ПС). На клавиатуре рабочей станции это кнопка с букавами SysReq. Возможно, кому-то покажется более удобным пользоваться именно этой клавишей.

Дальше идут стрелки (ARROWS):

  • LEFT - переход на символ влево;
  • RIGHT - переход на символ вправо.

Клавиши HOME и END осуществляют переходы на начало и конец строки.

Можно возвратить содержимое предыдущих командных строк. В памяти хранятся одновременно 16 команд (15 предыдущих и одна текущая, то есть та, которая набирается в данный момент), которые можно перебирать, двигаясь назад и вперед с помощью стрелок UP и DOWN:

  • UP - переход вверх на строку;
  • DOWN - переход вниз на строку.

Другие полезные клавиши:

  • TAB         - табуляция (переход на 8 символов) вправо;
  • BACK TAB    - табуляция влево;
  • InsCh       - вставка символа с подвижкой хвоста строки вправо;
  • DelCh       - удаление символа с подвижкой конца строки влево;
  • BACKSPACE   - замена символа слева на пробел с переходом на него курсора.

1.3.2. Режимы вставки и замены

Можно вводить символы в так называемом режиме замены, когда слово, набираемое поверх другого слова, заменяет его (как на пишущей машинке).

Можно, наоборот, пользоваться режимом вставки. В этом случае при наборе поверх старого слова вновь набираемые символы пишутся не вместо него, а перед ним, отодвигая хвост строки все дальше вправо.

Заметим, что сами разработчики предпочитают второй режим. Для того, чтобы сменить режим редактирования, нажмите CTRL Z.

 1.3.3. Переход в режим кириллицы

Для того, чтобы перейти с латиницы на кириллицу, нажмите последовательно левую, а затем правую кнопку SHIIFT (на клавиатуре она обозначена стрелкой вверх). Для перехода на латиницу эти кнопки нажимаются в обратной последовательности.



Часть 2. Файловая система ОС Excelsior

Глава 2.1. Корни и ветви дерева

Forest лес # Совокупность деревьев; удаление корневой вершины превращает дерево в лес.

А.Борковский
Англо-русский словарь по программированию и информатике

Тексты и коды программ, статьи и эта книжка – одним словом, вся информация, с которой работает программист, хранится на магнитных носителях в файлах.

2.1.1. Файл

Файл - это набор информации, имеющий имя; он представляет собой последовательность байтов на носителе (байт = 8 бит; например, код ASCII-символа занимает в точности один байт).

2.1.2. Директория

Директория - специальный вид файла; содержит список имен других файлов и указание на их местоположение в файловой системе.

Файловая система ОС Excelsior имеет древовидную структуру. Корневой директорией называется файл-корень файлового дерева.

2.1.3. Носитель

Носитель - единица совместного хранения файлов (обычно диск или дисковый пакет), устанавливаемая на любое подходящее устройство.



 Глава 2.2. Имя файла

- А для чего вообще нужны имена?

- Понятия не имею, - ответил Комар. - Дальше в лесу есть такое место, где ни у кого нет имени.

Л.Кэрролл. Алиса в Зазеркалье

У каждого файла есть имя, которое дается файлу при его создании. Имя файла - последовательность символов, за исключением символов " " (пробел), "/" (косая черта) и символа с кодировкой 0. Все остальные символы можно использовать, хотя использование некоторых символов приводит к неудобствам. Для начала рекомендуем пользоваться только печатными символами. Смысл этих ограничений станет ясен позже. Имя файла должно быть не длиннее 32 символов. Например:

my_file @@@ 2*2=4 и так далее.

2.2.1. Расширители имен файлов

Часто имена файла снабжаются расширителем. Расширитель имени файла - последовательность литер, отделяемая от имени символом "." (точка). Например: util.doc - расширитель - doc. Существуют также стандартные расширители для некоторых специальных файлов, принятые в ОС Excelsior.

  • .d - расширитель для файла, содержащего определяющий модуль (DEFINITION MODULE);
  • .m - расширитель для файла, содержащего реализующий модуль (IMPLEMENTATION MODULE);
  • .cod - расширитель для файла, содержащего код модуля;
  • .sym - расширитель для симфайла;
  • .ref - расширитель для реффайла.

Заметим, что директориям не принято давать имена с расширителями.

2.2.2. Полное имя файла

Местоположение каждого файла в файловом дереве определяется последовательностью директорий, которые требуется пройти системе до этого файла. Точкой отсчета при этом может быть как текущая директория, так и корневая директория. Это позволяет ввести понятие полного имени файла. Полное имя – имя файла с указанием маршрута к этому файлу от корня файлового дерева:

<полное имя файла> ::= ["/"]{<директория>"/"}<имя файла>

Например:

/doc/util1.doc

/users/andy/ccScan.m

/bin/StdIO.cod

Кроме того, местоположение файла можно определить, указав маршрут до него от текущей директории. Итак, определим маршрут:

<маршрут> ::= [ "/" | "." ] { ".."/ } { <директория>"/"}


Замечание.  Возможно, читатель удивится, увидев слово "маршрут" вместо привычного слова "путь". Leopold предложил использовать именно этот термин во избежание путаницы (что не удалось переводчикам документации по ОС UNIX). А что такое путь в ОС Excelsior - читайте дальше.

2.2.3. Соглашения об именах

В ОС Excelsior приняты следующие соглашения для имен файлов:

  • имена директорий в полном имени разделяются символом "/" (косая черта);
  • именем корневой директории является строка "/";
  • имя текущей директории обозначается символом "." (точка);
  • имя родительской директории обозначается символом ".." (две точки), который можно повторить несколько раз, двигаясь при этом к корню файлового дерева.


Глава 2.3. Прогулки по файловому дереву

Сунул я руку в карман, вытащил горсть гаек. Показал их Кириллу на ладони и говорю:

- Мальчика с пальчик помнишь? Проходили в школе? Так вот сейчас будет все наоборот. Смотри! - и бросил первую гаечку. Недалеко бросил, как положено. Метров на десять. Гаечка прошла нормально. - Видел?

- Ну? - говорит.

- Не "ну", а видел, я спрашиваю?

- Видел.

- Теперь самым малым веди "галошу" к этой гаечке и в двух метрах до нее не доходя остановись, понял?

А.Стругацкий, Б.Стругацкий
Пикник на обочине

Для перехода на другую директорию служит команда 'cd' (cHANGE dIRECTORY). Команде необходимо указать маршрут до новой директории.

cd ["/"|"."] {"../"} {<директория>"/"} <имя_директории>

Например:

cd

- показывает маршрут до текущей директории от корня файлового дерева;

cd ..

- переход на директорию, содержащую текущую;

cd /

- переход на корневую директорию;

cd ../../users/my

- переход на директорию my, расположенную на директории users.

Заметим, что 'cd' является командой пользовательской оболочки shell, о которой можно почерпнуть подробные сведения в разделе "SHELL: ПОЛЬЗОВАТЕЛЬСКАЯ ОБОЛОЧКА ОС".



Глава 2.4. Создание и редактирование файла

2.4.1. Запуск редактора

Чтобы создать новый текст, требуется создать новый файл. Новые файлы создаются с помощью редактора текстов 'ex'. Редактор запускается так:

ex <имя_файла> [-ключ]

Ключ у редактора только один - 'w'. Он означает "запретить запись в файл", то есть файл открывается только для чтения (о ключах см. раздел 8.1).

Если редактор не находит файла с таким именем, то сообщает об этом и после вашего подтверждения нажатием клавиши ENTER создает новый файл. Если вы передумали, нажмите CTRL C, и файл не будет создан.

2.4.2. Как выйти из редактора

Если вы закончили набирать или исправлять текст, то для того, чтобы выйти из режима редактирования и сохранить все ваши исправления, нажмите одновременно CTRL и e (^E).

Если вы хотите, чтобы прежний текст остался неисправленным, или вам не требуется сохранить набранный файл, выходите из редактора без записи, набрав ^C. После этого у вас запросят подтверждение, и после Y или y (yes) произойдет выход из редактора. Если вы вдруг передумали, наберите N или n, чтобы остаться в редакторе.

2.4.2.1. Полезный совет

- Мне никогда, никогда не забыть, - заявил Король, - это кошмарное мгновенье!

- Ты непременно о нем забудешь, - сказала Королева, - если не запишешь его в записную книжку.

Л.Кэрролл. Алиса в Зазеркалье

Во избежание потерь при неожиданном сбое полезно время от времени записывать набираемый текст. Это можно сделать, не выходя из редактора. Нажмите последовательно кнопки F10 и w - и ваш текст спасен на диск.

2.4.3. Экран

Вы находитесь в редакторе. Перед вами чистая страница - экран, который вы можете заполнять по своему усмотрению.

В нашей системе экранное редактирование построено по принципу "что на витрине, то и в магазине", иначе говоря, программист имеет ровно то, что видит на экране. Для примера: редактор не делает различия между пробелами, набранными клавишей SPACE, и пробелами, получившимися с помощью TAB. Как на экране, так и в файле это будут просто пробелы. То же относится и ко всяческим контрольным символам - если символ не изображен на экране, программист может быть уверен, что его нет и в редактируемом файле.

Самая нижняя строка экрана - информационная. Она выглядит приблизительно так:

<<<SCREEN 10:14 INS off BELL on 3:59 p.m. 0071 FILE x.m >>>

Первые два числа в информационной строке – положение курсора на экране (номер строки, считая сверху, и номер знакоместа, считая слева, разумеется, с нуля). Далее идет сообщение, включен ли режим вставки (INS on - включен, INS off - выключен). Затем - сообщение, включен ли звуковой сигнал, затем - текущее время (post meridiam или ante meridiam, в зависимости от времени суток), далее - номер строки в файле, на которой находится курсор, и, наконец, имя редактируемого файла.

В процессе редактирования могут выполняться разные действия - например, запись редактируемого файла на диск, или форматирование текста, и всякий раз сообщение о выполняемом действии появляется в информационной строке.

2.4.4. Клавиатура

Литерные клавиши в текстовом редакторе работают так же, как и в однострочном командном редакторе.

Редактору приходится работать с самыми разными типами терминалов (Фрящик, Видеотон, Mera, Labtam, Elorg и т.д.), с разными клавиатурами и разными названиями клавиш на них. Поэтому, как мы уже условились раньше, рассказ будет вестись в терминах функциональных кнопок.

2.4.4.1. Драгоценные кнопочки

Кроме обычных управляющих кнопок, в редакторе выделены три кнопки с условными названиями GOLD, SILVER, BRONZE. Эти кнопки меняют смысл кнопки, которая нажата после них. На клавиатуре рабочей станции это кнопки F1,F10 и F2 соответственно. С кнопкой SILVER вы уже частично знакомы. (F10 'w' - запись вашего текста на диск без выхода из редактора).

Кнопка GOLD (F1) предназначена для "усиления" действия следущей кнопки, то есть она является точкой опоры для переворачивания мира. Например:

  • F1 "стрелка вправо" - переход на конец строки;
  • F1 "стрелка влево" - переход на начало строки;
  • F1 PageUp - переход на начало файла;
  • F1 PageDn - переход на конец файла.

Кроме этого, кнопка F1 поможет вам быстро набирать тексты программы и выполнять поиск и замену кусков текста (когда вы доберетесь до соответствующих разделов книги "ОС Excelsior для всех").

Кнопка F2 предназначена в основном для ваших собственных расширений возможностей редактора.

Подробности обо всем этом можно узнать из документации по редактору или с помощью утилиты "help". А пока перечислим действия всех управляющих кнопок вашей клавиатуры.

2.4.4.2. Перемещения по тексту

переход вверх на строку UP
переход вниз на строку DOWN
переход на символ влево LEFT
переход на символ вправо RIGHT
табуляция вправо TAB
табуляция влево BACK TAB
переход на 16 строк вверх PageUp
переход на 16 строк вниз PageDw
переход на начало текущей строки Enter
со стиранием хвоста текущей строки SysReq

2.4.4.3. Вставка и удаление

вставка символа с подвижкой хвоста строки вправо InsCh
удаление символа с подвижкой хвоста строки влево DelCh
замена символа слева на пробел с переходом на него курсора BACKSPACE
удаление хвоста строки F8
удаление хвоста строки с приклеиванием хвоста следующей F4
вставка строки с отламыванием хвоста на следующую строку F3
отмена последнего DelLn F1 F2

2.4.4.4. Операции над строками

дублирование хвоста строки F7
обмен хвостов вверх от курсора F5
обмен хвостов вниз от курсора F6
склеивание строк F1 F4
разрыв строки F1 F3
дублирование хвоста поверх следующей строки F1 F7

 2.4.4.5. Операции над единицами текста

переход к началу слова F1 UP
(F2 LEFT)
переход к концу слова F1 DOWN
(F2 RIGHT)
удаление слова F1 DelCh
переход к началу строки
а также
F1 LEFT
Home
переход за конец строки
а также
F1 RIGHT
End
переход на конец текста F1 PageDw
переход к началу текста F1 PageUp
форматирование текущего абзаца F10 1
центрирование заголовка F10 2
возврат к первоначальному содержимому строки BRONZE F1
   

2.4.4.6. Выход в другие режимы редактора

командный режим F1 F1
установка параметров (SETUP-монитор) F1 F10
вход в shell редактора (выход - ESC) F10 PgDn

2.4.5. Возможности редактора

После того, как вы освоитесь с клавиатурой, переходите к ознакомлению с возможностями редактора.

Редактор располагает следующими возможностями:

  • позиционирование в файле;
  • поиск в файле;
  • контекстная замена;
  • работа с файлами (переименование и пр.);
  • работа с областью текста;
  • установка макросов;
  • установка параметров редактирования;
  • форматирование абзаца и области текста;
  • запуск задач из shell редактора.

Подробное описание всего этого дается в книге "Утилиты ОС Excelsior", глава 'ex'.

Редактор - достаточно сложный инструмент. Чтобы овладеть им в совершенстве, нужно не только понять, как он работает, изучить его функции, но и привыкнуть к нему настолько, чтобы пользоваться его возможностями не раздумывая, автоматически. На это потребуется некоторое время, как требуется время, чтобы научиться игре на музыкальном инструменте.



Часть 3. Задачи для Кроноса

Глава 3.1. Запуск задач

Для запуска любой задачи в ОС Excelsior достаточно просто набрать на экране ее имя (с соответствующими параметрами, если они есть). Именем задачи служит имя кодового файла (файла с расширителем '.cod') программного модуля задачи. Вот как будет выглядеть запуск задачи "my_task", находящейся на вашей рабочей директории, скажем, "wrk":

/users/vasya/wrk/my_task

Приходится набирать довольно длинную строчку, чтобы указать системе, с какой именно директории она должна взять коды задачи. Но полное имя указывать не обязательно, если у вас установлен путь поиска кодофайлов.

 3.1.1. Путь поиска кодофайлов

Этот пункт можно пропустить при первом чтении.

Умея определять местоположение файла, можно установить очередность маршрутов при поиске файлов для запуска программ. Это позволит вместо полного имени файла указывать лишь его имя, даже если файл лежит на удаленной директории или на другом носителе.

Путь, на которых будут разыскиваться коды запускаемой задачи, устанавливается так:

BIN='"'[.] [..] {{'/'<имя_директории>}' '} '"'

Теперь задана последовательность полных имен директорий, на которых надо искать коды загружаемой программы, например:

BIN=" . .. /ii/моя_дир"

- файл будет разыскиваться сначала на текущей директории, затем - на директории, содержащей текущую, затем на директории '/ii/моя_дир'.

Можно узнать ранее установленный путь, набрав команду

set

Кроме всего прочего, в строчке "BIN" команда set показывает установленный путь.

3.1.2. Как запустить задачу

Итак, для запуска задачи достаточно указать имя кодофайла ее головного модуля:

my_task

и, если таковой будет найден на какой-нибудь из указанных пуем директорий, задача начнет исполняться.

Заметим, что если на текущей директории имеются файлы name.cod (кодофайл) и name.@ (командный файл с тем же именем, главу о командных файлах), то команда

name

приведет к исполнению командного файла name.@, а не задачи с кодофайлом name.cod. Как обойти это препятствие? Очень просто: запуская задачу, укажите имя ее кодофайла с расширителем:

name.cod

3.1.3. Как остановить задачу

Исполнение текущей задачи можно прервать нажатием CTRL_C. Разумеется, есть задачи, которые так просто не прервешь (например, пользовательская оболочка 'sh', которую запускает администратор системы). Случаям, когда CTRL_C "не действует", посвящена специальная глава в книге "ОС Excelsior для всех".

 3.1.4. Стандартный ввод-вывод

Запуская какую-либо задачу (утилиту, пользовательскую программу), вы ожидаете вывода ее результатов. Некоторые задачи требуют ввода дополнительной информации. Многие задачи пользуются стандартным вводом-выводом, который по умолчанию открыт на терминал, с которого была запущена задача.

Вывод может быть направлен в файл. Для этого при запуске требуется указать имя файла, куда будут записаны результаты, предварив его символом '>':

<имя_задачи> '>'<файл_результатов>

- результат записывается в указанный файл.

Если в файле лежала какая-то информация, она будет удалена. Чтобы этого не произошло, можно дописать результаты в файл с помощью значка '>>':

<имя_задачи> '>>'<файл_результатов>

- результат дописывается в указанный файл.

Ввод тоже может осуществляться из файла с помощью '<':

<имя_задачи> '<'<файл_ввода>

3.1.5. Параллельное исполнение задач

В ОС Excelsior есть возможность запустить несколько (число зависит от потребностей задачи в ресурсах) задач одновременно. С точки зрения операционной системы они совершенно равноправны с задачей, запускаемой "явно". После завершения "фоновая" задача освобождает выделенные ей ресурсы. Для обозначения таких задач существуют разные термины - параллельная, открепленная, независимая и т.д.. Мы будем использовать, пожалуй, последний.

3.1.5.1. Запуск независимой задачи

Запуск выглядит так:

<имя_задачи> &

После этого вы можете запускать следующую задачу в "явном" виде, ее исполнение не будет мешать исполнению ранее запущенной задачи.

Вам следует лишь позаботиться о выводе результатов, если результатом является, например, печать на экран, чтобы вывод не мешал вашей работе с "явной" задачей.

Открепить уже исполняющуюся задачу можно нажатием CTRL_X.

3.1.5.2. Перечень задач

Можно ознакомиться с перечнем всех задач, исполняемых компьютером в данный момент. Это делается с помощью команды shell'а ps:

ps

На экран будет выдано приблизительно следующее:

0066        run    ex            shell.doc

0043        run    shell

В первой колонке указан номер задачи, в третьей - ее имя. Полное описание команды ps дается в разделе, посвященном пользовательской оболочке ОС, в книге "Кронос для всех".

3.1.5.3. Прекращение исполнения независимой задачи

Прекратить исполнение можно командой shell'а kill :

kill [<номер задачи>]

, например:

kill 1031

или

kill

Если номер задачи не указывать, то вам предложат на удаление все ваши (запущенные на текущем терминале) задачи.



Глава 3.2. Утилиты

Выше нам уже пришлось употребить слово "утилита", не поясняя его значения. Утилита - это программа, предназначенная для выполнения какого-нибудь действия (или последовательности действий). Точным переводом этого слова было бы "пользилка". Утилиты могут иметь самые разные назначения: бывают утилиты для работы с файлами, для работы с устройствами, и редактор, с помощью которого редактируются тексты, и Модула-компилятор - тоже утилиты. Попросту говоря, утилиты - это приспособления, с помощью которых программист имеет доступ к операционной и файловой системам и т.д..

 3.2.1. Ключи

Обычно утилита выполняет не одно, а несколько сходных между собой действий. Выполняемое действие зависит от ключа, с которым запущены утилита. Так, например, утилита 'ls', запущенная без ключей, выдает на экран имена файлов на указанной путем директории, кроме скрытых файлов (например, с расширителями .sym, .ref и .cod).

Будучи запущена с ключом -a

ls -a

ls выдает имена ВСЕХ файлов, в том числе скрытых.

ls -l

выдает имена файлов в "длинном формате", то есть с указанием обширной информации о каждом файле, например:

--rwxr-x--- 1 968 Jan 18 13:08 a.m

Это значит, что на текущей директории находится только один файл a.m c указанными битами защиты, привязанный к одному имени, длиной 968 байт, последнее изменение внесено 18 января в 13.08. И так далее.

Кроме символьных, существуют еще числовые ключи. С их помощью утилите можно передавать числовые параметры. Пример числового ключа:

where *.cod a=161288

- утилита находит все кодофайлы, запись в которые производилась после 16.12.1988.

3.2.2. Запуск утилиты

Таким образом, запуск утилиты выглядит так:

запуск          ::= <имя_утилиты> {<параметр>} {<числовой_ключ>} ['-'/'+'{<ключ>}]

<параметр>      ::= <строка>

<числовой_ключ> ::= <строка>'='<число>

<ключ>          ::= <строка>

Заметим, что все параметры и ключи утилит могут располагаться в командной строке в произвольном порядке.

3.2.3. Подсказка

Набор утилит относительно стабилен, но может пополняться за счет ваших собственных полезных программ, если они будут соответствовать стандарту, принятому в ОС Excelsior.

В частности, этот стандарт требует, чтобы каждая утилита имела ключ 'h', по которому она выдает краткую подсказку о себе самой (назначение и ключи).

Подробнее об этом и об утилитах вообще рассказывается в томе "Утилиты ОС Excelsior", который здесь тоже упоминался.

Помимо того, что каждая утилита выдает краткую информацию о себе по ключу 'h', существует еще утилита, ведающая службой подсказки в ОС Excelsior. Эта утилита оказывает на первых порах большую помощь, она так и называется - help. Запустив help, вы попадаете в меню, состоящее из названий всех утилит системы (а также команд shell - пользовательской оболочки). О каждой утилите вы можете получить полную информацию – от порядка запуска и этимологии названия до ключей и примеров ее употребления. О том, как пользоваться help, подскажет она сама.



Глава 3.3. Командный файл

Несколько командных строк могут быть объединены в командный файл, который создается с помощью редактора и при запуске выполняется, как если бы его строки были набраны последовательно с терминала. Имя командного файла имеет расширитель @:

<имя_файла>.@

 3.3.1. Запуск командного файла

Запуск командного файла осуществляется набором имени файла:

<имя_файла>

Если на одной директории находятся кодофайл и командный файл с одним и тем же именем, то при наборе имени файла запустится кодофайл. Избежать путаницы можно, набрав имя файла с расширителем:

my_file.@

- будет исполняться командный файл;

my_file.cod

- исполнится кодофайл.

3.3.2. Прекращение командных файлов

Исполнение командного файла прекращается нажатием CTRL_C. При этом прекращаются все запущенные в нем задачи.

3.3.3. Соглашения для командных файлов

Для командных файлов приняты следующие соглашения:

1) строка комментария предваряется символом % ;

2) параметры, передаваемые командному файлу, обозначаются $1, $2, ..., $9. Например, если командный файл user.@ выглядит так:

cd /users

cd $1

, то $1 при исполнении командного файла заменяется на переданный ему параметр:

user ВАСЯ

, что равносильно последовательности команд

cd /users

cd ВАСЯ

3.3.4. Пример командного файла

Приведем пример командного файла:

% Командный файл архивирования

% Смонтировать диск fd0 на директорию /mnt:

mou /mnt /dev/fd0

% Скопировать все изменившиеся и недостающие файлы на всех

% поддиректориях текущей директории на архивную директорию

% с сохранением имен файлов и созданием недостающих

% директорий


cp .//$1 /mnt/$@/$1 -qdb


% Демонтировать диск с директории /mnt:


mou /mnt -r


% Конец архивирования

3.3.5. Командный файла пользователя

На вашей приватной директории хранится командный файл profile.@. Такой файл предназначается для перехода на вашу рабочую директорию, установку удобных для вас параметров командной строки. Выглядит он приблизительно так:

% Установить путь поиска кодофайлов:

BIN=". /usr/flm/bin /bin /usr/bin"


% Установить путь поиска командных файлов:

ETC=". /usr/flm/bin /etc /usr/etc"


% Установить приглашение:

PROMPT="%t %/> "


% Перейти на директорию wrk:

cd wrk


% Mы гостям хорошим рады,

% Смело в дом входите,

% Вытирайте ноги, гады,

% Чистоту блюдите.

Запуск командного файла пользователя осуществляется системой после входа в систему.

Если вы меняли обстановку работы, и вам после этого захотелось домой, в свою родную обстановку, наберите команду, которая так и называется: home - домой, указав, где именно ваш дом:

home /usr/ВАСЯ

Если на указанной вами директории обнаружится ваш личный "profile", он будет исполнен, и вы окажетесь у себя на директории в привычной обстановке.



Часть 4. Создание программ

Программирование - это процесс исправления старых ошибок и насаждения новых.

А.Борковский
Из частной беседы

Глава 4.1. О языке Модула-2

Автором языка Модула-2 является Никлаус Вирт, профессор Высшей Технической Школы (Цюрих), который реализовал его на своей машине Лилит. Унаследовав лучшие черты языка Паскаль, Модула-2 имеет ряд отличительных особенностей, которые делают ее незаменимой при разработке программного обеспечения. Прежде всего это, конечно, модульность (и возможность разбиения модуля на определяющую и реализующую части), развитые структуры данных и управления, строгий контроль типов, наличие процедурных типов, что позволяет динамически параметризовать процедуры внешними действиями, а также наличие средств программирования низкого уровня, позволяющих ослабить жесткий контроль типов и отображать структурные данные на память.

Знание Модулы-2 на уровне языковых конструкций еще не дает возможности существенно использовать ее преимущества, поэтому, прежде чем писать тексты программ, тщательно ознакомьтесь с описанием языка. Лучше всего это сделать по книжке "Программирование на языке Модула-2" Н.Вирта в переводе В.А.Серебрякова и В.М.Ходукина, выпущенной в серии "Математическое обеспечение ЭВМ" издательством "Мир" в 1987 г.

Все дальнейшее изложение будем вести в предположении, что вы знакомы с языком.



Глава 4.2. Как пользоваться библиотеками

4.2.1. Модульность и библиотеки

Коль скоро вы собираетесь работать на Кроносе и писать свои программы на Модуле-2, нет смысла убеждать вас в пользе модульности. И все же скажем несколько слов о том, как отразилось это свойство на системе программирования, а именно ее части, именуемой библиотеками стандартных процедур.

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

Итак, библиотеки

  1. экономят дисковое пространство;
  2. ускоряют написание программы в сотни раз;
  3. позволяют не изобретать велосипед;
  4. дают возможность писать свои программы чужими руками.

4.2.2. Назначение и название

За время существования ОС Excelsior написано, упразднено и переписано заново множество библиотек. Чтобы овладеть всем этим богатством, необходимо знать его. Лучший способ - изучение определяющих модулей библиотек с подробными комментариями. Все это можно найти в справочном томе "Библиотеки ОС Excelsior". Там же приводится список всех библиотечных модулей с указанием их назначения.

Для начала рекомендуем ознакомиться с библиотеками StdIO, Strings, Image, Args, Streams. В этих библиотеках содержатся все процедуры, которые могут понадобиться для написания простых программ.

4.2.3. StdIO - стандартный вывод

Наиболее необходимая библиотека (во всяком случае, для начинающих) - библиотека стандартного вывода StdIO. С помощью ее процедуры print можно вывести информацию в указанном формате в так называемый файл стандартного вывода. По умолчанию это экран, но вывод может быть перенаправлен стандартным способом (см. в главе 7 раздел 3).

Остановимся на этой процедуре подробно.

PROCEDURE print(format: ARRAY OF CHAR; SEQ args: WORD);

Первый аргумент этой процедуры - формат, в котором требуется вывести перечисленную далее последовательность аргументов. Конструкция SEQ (о реализованных расширениях языка Модула-2 см. выше п.10.3) означает перечисление произвольного числа аргументов (возможно, ни одного).

Например,

print("Hello, hacker!")

напечатает строчку

Hello, hacker!

Последовательность аргументов у процедуры опущена.

-format- задается строкой ASCII-8 символов (и тогда заключается, естественно, в кавычки " или '), или строковой переменной (и тогда байт 0с означает конец строки).

Все символы строки выдаются в стандартный вывод без изменения, за исключением некоторых символов, которые процедура print интерпретирует специальным образом.

1. Символ \ интерпретируется следующим образом:

\n отрабатывается как CR+LF.

\r отрабатывается как CR.

\l отрабатывается как LF.

Если в строке требуется символ '\', предварите его '\': '\\' преобразуется в просто '\'.

ВНИМАНИЕ! \n, \r, \l обрабатываются ТОЛЬКО в строке -format-, но не в строках-аргументах!!!

2. Символ % и следующие за ним интерпретируются процедурой как формат, в котором требуется вывести соответствующий аргумент процедуры из последовательности аргументов.

Если требуется символ '%', предварите его '%': '%%' преобразуется в просто '%'.

Таким образом, формат начинается символом %, далее идут модификаторы, а завершается формат базой. Формат может встречаться в строке -format- несколько раз (возможно, ни разу).

формат ::= %{ модификатор }база.
модификатор ::= ( space | "$" | "-" | "+" | "#" | ширина | точность } .
ширина ::=
  -
цифра { цифра } | "*".
задает общую(!) ширину поля (с учетом возможных символов основания, знака и т.п.); если указанной ширины недостаточно для представления выводимого значения, происходит автоматическое расширение поля до минимально необходимого.
точность ::=
  -
"." цифра { цифра } | ".*" .
задает число значащих цифр после(!) запятой в форматах 'f','e','g' или число символов строки в формате 's';
Замечание.  Если вместо спецификации ширины и/или точности указаны '*', то значения ширины и точности нужно указать в соответствующих аргументах, при этом соблюдается следующий порядок: сначала аргумент-строка, которую требуется вывести, а затем аргументы-модификаторы.
Замечание.  Значения точности и ширины должны быть из диапазона [0..255]; в противном случае они принимаются равными значению по умолчанию.
#   - показывает число с указанием основания (например: image(s,"%$10#h",12abcdefH) эквивалентно image(s,"012ABCDEFh") );
-   - число пишется слева в поле установленной ширины;
+   - показывает число со знаком, независимо от знака числа;
$ (zero)   - дополнить до нужного количества разрядов ведущими нулями;
space   - выставляется знак, если число отрицательное, иначе пробел;
база ::= ("d"|"h"|"x"|"b"|"o"|"s"|"c"|"{}"|"i" |"f"|"e"|"g").
d (Decimal)   - десятичное;
h,H (Hexidecimal)   - шестнадцатеричное (h,x -- "A".."F" прописные);
x,X (Hexidecimal)   - эквивалентно 'h' (H,X -- "a".."f" строчные);
b,B (Octal)   - восьмеричное;
o,O (Octal)   - эквивалентно 'b';
s,S (String)   - строка;
c,C (Char)   - одиночный символ;
{} (set)   - битсет;
i,I (bIn)   - двоичное;
f,F (Float)   - вещественное число в формате:
    [+|-]dddddddd.ddddd
      |__  n1  __|_ t _|
Число цифр до запятой n1 – минимально необходимое для представления числа. Число цифр после запятой t – задается точностью (по умолчанию 6).
e,E (Exponent)   - вещественное число в формате:
    [+|-]d.ddddddE(+|-)dd   или
    [+|-]D.DDDDDDe(+|-)DD
Число цифр до запятой 1. Число цифр после запятой - t – задается точностью (по умолчанию 6).
(формат 'e' - 'E' в результате; формат 'E' - 'e' в результате);
g,G (General)   - вещественное число в формате:
если FLOAT(TRUNC(число))=число, то в формате dddddd; иначе в формате 'f' или 'e' - какой короче.

Замечание. 

Модификаторы '$' и '-', естественно, не совместимы.

Модификаторы '+' и space, естественно, не совместимы.

Модификатор точность может быть использован только с базами 's','f','e','g'.

Модификаторы '+' и space могут быть использованы только с базами 'i','f','e','g'.

Модификаторы '$', '+' и space НЕ могут быть использованы с базами 's','{}','c'.

4.2.3.1. Примеры употребления процедуры print

Изложение примеров будем вести в следующей форме:

Текст программы Будет выведено
1) Примеры без аргументов
print("Hello, hacker!\n"); Hello, hacker!
print("Hello, "0c"hacker!"); Hello,
print("Hello,\nhacker!"); Hello,
hacker!
print("Hello,\lhacker!"); Hello,
      hacker!
VAR h: ARRAY [0..79] OF CHAR;
  h:= "Hello!"
  print(h);
Hello!
2) Примеры с аргументами
VAR n1,n2 : ARRAY [0..79] OF CHAR;
BEGIN
  n1:="Вася!"
  n2:="Петя!"
  print("Hello, %s\n",n1);
  print("Hello, %s\n",n2);
Hello, Вася!
Hello, Петя!
3) C использованием модификатора "ширина"
VAR str: ARRAY [0..79] OF CHAR;
BEGIN
  str:="Вася!"
  print("Hello, %10s\n",str);
Hello,     Вася!
VAR str: ARRAY [0..79] OF CHAR;
     i : INTEGER;
BEGIN
  str:="Вася!"
  i:= 10;
  print("Hello, %*s\n",str,i);
Hello,     Вася!


Глава 4.3. Компиляция

Наконец текст вашей программы готов. Чтобы Кронос исполнил программу, нужно представить ее в виде последовательности машинных команд (каких именно - см. в книге "Архитектура семейства процессоров КРОНОС" описание системы команд). Функцию перевода текста программы на языке Модула-2 в последовательность команд осуществляет Модула-2 компилятор.

Кроме этой основной задачи, компилятор выполняет еще одну важную функцию - он проверяет правильность текста программы, сообщая о синтаксических и семантических ошибках (какие именно ошибки "отлавливает" компилятор, видно из п.12.3).

В этой главе мы изложим только начальные сведения о компиляции. Более подробно мы остановимся на Модула-компиляторе в книге "ОС Excelsior для всех".

4.3.1. Запуск компилятора

Запуск компилятора осуществляется с помощью утилиты mx, либо с помощью компилирующего редактора ex, который в дальнейшем мы будем называть по аналогии с TURBO-PASCAL фирмы Borland турбо-компилятором, или просто турбиной. Он отличается от текстового редактора возможностью откомпилировать программу, текст которой вы редактируете в данный момент, не записывая его на диск и не покидая редактора. В этом случае компиляция запускается последовательным нажатием клавиш F10 и m.

Модула-компилятору передается текст программы, оформленный в соответствии с описанием языка. В системе принят стандартный расширитель для имени файла, содержащего определяющий модуль - .d ; реализующий или программный модуль - .m . Если расширитель отсутствует, компилятор добавляет расширитель .m : program.m.

Сначала компилируют определяющий модуль, если он есть, затем - реализующий и только потом программный.

Если программа состоит из нескольких модулей, то компилируют сначала все определяющие модули, а затем реализующие.

4.3.2. Что получается в результате компиляции

В результате компиляции определяющего модуля (DEFINITION MODULE) получается символьный файл (симфайл) и файл ссылок (реффайл); реализующего модуля (IMPLEMENTATION MODULE) - реффайл и кодовый файл (кодофайл); программного модуля (MODULE) - реффайл и кодофайл.

В симфайле содержится информация для компилятора; в реффайле - для отладочных утилит (например, для утилиты hi (history), выдающей историю последнего неудавшегося процесса. Кодофайл содержит собственно исполняемый код программы.

Имена симфайлов, реффайлов и кодофайлов получаются из имени модуля (не из имени файла) добавлением расширителей '.sym', '.ref' и '.cod' соответственно.

Обратите внимание, что обнаружить симфайлы, реффайлы и кодофайлы на директории с помощью утилиты ls можно лишь запустив ее с ключом -a (all).

4.3.3. Сообщения компилятора

Компилятор выдает на русском языке сообщение о характере ошибки, приводится номер строки и сама строка, в которой допущена ошибка, а также помечается предположительно место в строке, где надо искать ошибку (в случае турбо-компилятора происходит последовательное позиционирование курсора на место ошибки), например:

Ожидался символ ';'

104: PROCEDURE Next(i,j: INTEGER$, A: ARRAY OF CHAR);

Число ошибок: 1

Если в процессе компиляции не обнаружено ошибок, на экран выдается сообщение:

lines 247 time 09cp + 11io help.cod 326 words

, что означает, что был скомпилирован модуль размером 247 строк, время компиляции 9 секунд, время записи - 11 секунд, кодофайл записан в файл help.cod и объем кода 326 слов.

В результате компиляции на текущей директории появятся файлы help.ref и help.cod.

4.3.3.1. Для турбо-компилятора

Если вы запустили компиляцию из турбины, компилятор сообщит количество ошибок и спозиционируется на первой встреченной ошибке, указав ее характер. После ее исправления перейти к следующей ошибке можно последовательным нажатием клавиш F10 и +.

После того, как все ошибки исправлены и компиляция прошла успешно, не забудьте записать текст программы (F10 w).



 Книга вторая

ОС Excelsior ДЛЯ ВСЕХ



Первое предисловие соавтора

Разумеется, книга эта не могла бы появиться на свет без поддержки и помощи некоторых людей. Говорю я об этом для того, чтобы ответственность за нее разделить на всех поровну.

Д.Даррелл. Моя семья и другие звери

Известно, что авторство отличается от соавторства приблизительно тем же, чем пение от сопения. Тем не менее мне пришлось взять на себя неблагодарную миссию быть соавтором сразу целой группы авторов - разработчиков software для процессоров Кронос. Никто не может лучше рассказать о своем детище, чем его родитель. К сожалению, рядовому читателю редко бывает нужно "лучше", он хочет - понятнее. Насколько мне удалась скромная роль переводчика - судить читателю.

Говорю все это прежде всего для того, чтобы снять с себя всякую ответственность за написанное здесь (или хотя бы частично взвалить ее на авторов). Далее в подобных предисловиях будет указано, с кого именно спрашивать за тот или иной раздел.

Соавтор



Часть 5. Система программирования Модула-2

Предисловие соавтора

За эту часть надо спрашивать с А.Недори, который, к тому же, является создателем Модула-компилятора для Кроноса.

Соавтор

Система программирования Модула-2 представляет собой систему из четырех относительно независимых компонент:

  1. компилятора с языка Модула-2;
  2. универсального редактора текстов;
  3. средств отладки программ;
  4. набора библиотек стандартных процедур.

Универсальному редактору текстов посвящен специальный раздел.

Набор библиотек стандартных процедур, а также способы их использования описаны в справочнике "Библиотеки ОС Excelsior".

В этом разделе описывается то, что касается создания, компиляции и отладки Модула-программ: версия языка Модула-2, реализуемая компилятором "mx" для процессоров семейства Кронос, ограничения и расширения языка, способы запуска и структура компилятора, необходимое компилятору окружение, визуализатор программ и посмертный историк.



Глава 5.1. Входной язык компилятора

Язык Модула-2 реализуется компилятором в соответствии с сообщением о языке [5] и изменениями, внесенными Н.Виртом [2].

Кроме того, реализованы некоторые расширения языка. Часть изменений была внесена в язык под влиянием дальнейших работ Н.Вирта [3] и Проекта стандарта языка [4]. Компилятор не соответствует ISO-стандарту языка Модула-2, поскольку таковой стандарт в данное время отстутствует.

5.1.1. Ограничения компилятора

Список требований к компилятору взят из проекта [4]. Символом '*' помечены пункты, не соответствующие требованиям проекта. В скобках () приведены рекомендации проекта.

  • * размер множеств не более 32 бита     (256, SET OF CHAR);
  • число параметров процедуры не более 256               (8);
  • число импортируемых модулей не ограничено            (32);
  • вложенность процедур и модулей не ограничена          (8);
  • вложенность описания записей не ограничена          (256);
  • вложенность описания массивов не ограничена         (256);
  • вложенность вызовов процедур не ограничена          (256);
  • число альтернатив в операторе выбора не более 256   (256);
  • длина идентификатора не ограничена                  (256);
  • число экспортируемых объектов не ограничено          (16);
  • длина строкового литерала не более 256               (80);
  • вложенность операторов не ограничена                  (8);
  • размер перечислимого типа не ограничен              (128).
Примечание:  параметры типа открытый массив (ARRAY OF) и параметры-последовательности (SEQ-параметры) считаются за два параметра.

Дополнительные ограничения:

  • число параметров у кодовой процедуры не более 7.

5.1.2. Изменения, внесенные в язык

В 5.1.2.1 содержится краткий список изменений. В 5.1.2.2 приводится описание изменений, внесенных в язык, базирующееся на Сообщении [5]. Описание выдержано в стиле Сообщения [5] с точностью до порядка нумерации разделов.

Полный синтаксис входного языка приведен в 5.1.2.3.

Примеры программ, демонстирующие расширения, приведены в 5.1.6.



5.1.2.1. Список изменений

  1. Неявная конкатенация строк.
  2. Дополнительный синтаксис комментария.
  3. Отсутствуют стандартные типы CARDINAL, LONGINT, LONGREAL.
  4. Стандартный тип STRING.
  5. Тип динамический массив.
  6. Новые операции для целых "/" и REM.
  7. Операция инвертирования множества.
  8. Операции циклического сдвига.
  9. Конструктор массивов.
  10. Передача параметров по доступу.
  11. Параметры-последовательности.
  12. Кодовые процедуры.
  13. Новые стандартные процедуры ASSERT, BITS, BYTES, LEN, REF, NEW, DISPOSE, RESIZE.
  14. Переименование при импорте.
  15. Все, что касается процессов и сопрограмм, реализуется средствами ОС, а не компилятора.
  16. Настраиваемая динамическая поддержка.

5.1.2.2. Дополнения к Сообщению о языке Модула-2

Данный пункт содержит описание изменений входного языка, базирующееся на Сообщении [5]. При описании изменений указывается пункт сообщения, к которому оно относится, например, <8.2.4>. Если описание расширения должно быть выделено в новый пункт по логике построения сообщения, то такой пункт помечается символом "*".


<3> Словарь и изображение

2. Внесены изменения в синтакис Целого (см. 5.1.2.3) для того, чтобы описать изменения в следующем пункте.

3. Расширен синтаксис цепочки. Цепочка есть последовательность строк и представлений литер, начинающаяся со строки.

$ Цепочка = Строка { Строка | ПредставлениеЛитеры }.

$ Строка = "'" { Литера } "'" | '"' { Литера } '"'.

Примеры:

"Привет" 12c 15c 'читателю'

'' 33c 'H' 33c 'J'

4. Дополнительные зарезервированные слова:

CODE

DYNARR

FORWARD

REM

SEQ

VAL

5. Дополнительные синтакисис комментария: от "--" до конца строки. Внутри комментария одного вида комментарий другого вида не рассматривается.

Примеры:

1)

-- (*

i:=0; -- этот оператор не в комментарии;

-- *)

2)

(* -- *) i:=0; -- и этот оператор не в комментарии.


<4> Описания и правила видимости

Отсутствуют стандартные идентификаторы:

CARDINAL

LONGINT

LONGREAL

VAL

Добавлены стандартные идентификаторы:

ASSERT  (10.2.1*)

BITS    (10.2.1*)

BYTES   (10.2.1*)

DISPOSE (10.2.2*)

LEN     (10.2.1*)

NEW     (10.2.2*)

REF     (10.2.1*)

RESIZE  (10.2.2*)

STORAGE (14.2*)

STRING  (6.9)


<6> Описание типов

Добавлен тип динамический массив (динмассив).


<6.1> Основные типы

Отсутствует типы CARDINAL, LONGINT, LONGREAL.


<6.8> Тип процедура

$ ТипПроцедура ╜ PROCEDURE [ СписокФормТипов ].

$ СписокФормТипов = "(" [[VAR] ФормТип

$     { "," [VAR] ФормТип } ] [ SEQ [VAR] КвалИдент ] ")"

$     [ ":" КвалИдент ].

Последний параметр в списке формальных параметров может быть последовательностью параметров (см. <10.1>).


<6.9*> Тип динмассив

Тип динмассив является обобщением типа указателя на случай указателя на открытый массив.

$ ТипДинМассив = DYNARR OF Тип.

DYNARR OF T следует рассматривать как POINTER TO ARRAY OF T.

Значение типа динмассив состоит из дескриптора и тела. Дескриптор динмассива содержит два поля ADR и HIGH, описывающие соотственно адрес начала массива и верхнюю границу массива. Динмассив почти всегда ведет себя как открытый массив. Значениями индекса могут быть целые числа (INTEGER) в диапазоне от 0 до верхней границы.

Стандартный тип STRING определяется следующим образом:

STRING = DYNARR OF CHAR

Примеры:

DYNARR OF CHAR

DYNARR OF STRING

Примеры использования динмассивов рассматриваются в 5.1.3 и 5.1.6. См. также пункты <8.1>, <10.2>.


<8> Выражения

В <8.1> рассматриваются изменения, касающиеся динмассивов, в <8.2> - новые операции. Новый пункт <8.3*> посвящен конструкторам массивов.

<8.1> Операнды

Если структура - динмассив D, то запись D[E] обозначает элемент D, индекс которого - текущее выражение значения E. Обозначение вида D[E1,E2,...En] означает то же, что и D[E1][E2]...[En]. Запись D^ означает дескриптор динмассива, то есть запись с двумя полями ADR и HIGH. Запись D^.f означает поле f дескриптора D (f должно быть ADR или HIGH).

Примеры:

str : STRING

text: DYNARR OF STRING


str[i]    (CHAR)

text[i]   (STRING)

text[i,j] (CHAR)

str^      (см. примечание)

str^.ADR  (ADDRESS)

str^.HIGH (INTEGER)

Примечание:  Каждому типу динмассива соответсвует уникальный анонимный тип дескриптора вида:

RECORD

  ADR : SYSTEM.ADDRESS;

  HIGH: INTEGER;

END;


<8.2> Операции

Введены дополнительные операции типа умножения и унарная операция инвертирования множества (<8.2.3>). Операции REM и "/" для целых рассматривается в <8.2.1>, а операции "<<" и ">>" в <8.2.5*>.

$ ОперацияТипаУмножения = "*" | "/" | DIV | MOD | AND | REM | "<<" | ">>".


<8.2.1> Арифметические операции

В соответствии с Проектом стандарта [4] (см. п.6.6.1.2 Проекта) введены целые операции "/" и REM для целого деления и взятия остатка соответственно.

Выполняются соотношения:

(-x/y)=(-x/y)=-(x/y)       (для y#0)

x=(x / y)*y+(x REM y)      (для y#0)

x=(x DIV y)*y+(x MOD y)    (для y>0)

Результат операции x REM y определен при y#0 и является либо нулем, либо целым числом, совпадающим по знаку с x и меньшим по абсолютной величине, чем y.

Результат операции x MOD y определен при y>0 и является неотрицательным целым числом, меньшим y.

Примечание.  Для процессоров Кронос 2.2 операции DIV и MOD реализованы некорректно и эквивалентны операциям /, REM. Подробнее см. 5.1.4.2.

<8.2.3> Операции над множествами

Унарная операция "-" обозначает побитовую инверсию множества. Результат операции имеет тип операнда.


<8.2.5*> Операции циклического сдвига

Операции циклического сдвига ">>" и "<<". Левый операнд - любого типа длиной 1 слово, правый должен быть типа INTEGER. Тип результата операции ясен из таблицы:

Тип левого операнда Тип результата
INTEGER INTEGER
BITSET BITSET
все остальные WORD

<8.3*> Конструктор массивов

Расширен синтаксис множителя. В качестве множителя можно использовать конструктор массива.

$ Множитель = .... | Конструктор.

$ Конструктор = ARRAY OF КвалИдент

$ "{" КонстВыражение { "," КонстВыражение } "}".

Конструктор ARRAY OF T{E1,E2,...En} обозначает константное значение массива типа ARRAY [0..n-1] OF T, где n – число выражений. Тип элемента должен быть основным типом, типом перечисления, диапазона или множества.

Пример:

CONST

array = ARRAY OF INTEGER{ 1,2,3 }

element = array[1];


<9.2>. Вызовы процедур

Последним формальным параметром процедуры может быть параметр-последовательность (SEQ-параметр). При вызове вместо такого параметра может быть подставлена последовательность фактических параметров любой длины (в том числе и пустая). Каждый из этих фактических параметров должен быть обозначением переменной для случая последовательности переменных (SEQ VAR) и выражением в случае последовательности значений (SEQ). Совместимость как в случае обычных параметров.

Вместо последовательности параметров в качестве такого SEQ-параметра может быть подставлен формальный параметр-последовательность. При этом их типы должны совпадать и признак VAR должен быть у обоих или у обоих отсутствовать.

В случае, если тип параметра-последовательности есть WORD, в качестве фактических параметров можно использовать параметры любого типа. При передаче структурных параметров передается их адрес, а если параметр имеет тип динмассив, то передается адрес массива (а не адрес дескриптора).


<9.2.1*> Вызов кодовых процедур

При вызове кодовой процедуры фактические параметры вычисляются в соответствии с заголовком процедуры и загружаются на стек. Далее выполняется код, полученный текстуальной вставкой тела кодовой процедуры в место вызова. Компилятор не контролирует корректность работы кодовых процедур со стеком.


<10>. Описание процедур

$ ОписаниеПроцедуры = ЗаголовокПроцедуры ";"

$       ( Блок | Код ) Идентификатор.

$ Код = CODE { КонстВыражение } END.

$ ЗаголовокПроцедуры = PROCEDURE Идентификатор

$       [ ФормальныеПараметры ].

$ Блок = { Описание } [ BEGIN ПослОператоров ] END.

$ Описание = CONST { ОписаниеКонстанты ";" } |

$            TYPE { ОписаниеТипа ";" } |

$            VAR { ОписаниеПеременной ";" } |

$            ОписаниеПроцедуры ";" | ОписаниеМодуля ";"

$            ЗаголовокПроцедуры ";" FORWARD ";".

В соответствии с [2,4] разрешены предварительные описания процедур. Предварительное описание представляет собой заголовок процедуры, за которым следует зарезервированный идентификатор FORWARD. Полное описание процедуры, включающее тело процедуры, должно появиться в той же области действия, что и предварительное описание, и на том же уровне видимости. Типы формальных параметров у предварительного и полного описания процедуры должны быть совместимы, а в случае открытых массивов совместимы должны быть базовые типы массивов.

Введены кодовые процедуры. Тело кодовой процедуры представляет собой последовательность байтов. Каждый байт задается целым выражением в диапазоне 0..255. (см. <9.2>). число параметров у кодовых процедур ограничено (см. 5.1.1).

Запрещается предварительное описание кодовых процедур и экспорт из определяющего модуля.


<10.2> Формальные параметры

$ ФормальныеПараметры =

$      "(" [ ФПСекция { ";" ФПСекция } ]

$       [ SEQ [ VAR ] Идентификатор ":" КвалИдент ] ")"

$       [ ":" КвалИдент ].

$ ФПСекция = [ VAR | VAL ] СписИдент ":" ФормТип.

$ ФормТип = [ ARRAY OF ] Квалидент.

Введен новый способ передачи параметра - передача по доступу. Этот способ передачи указывается только при полном описании процедуры (нельзя использовать в процедурном типе и в предварительном описании процедуры). С точки зрения вызова передача параметра по доступу эквивалентна передаче параметра по значению. Формальный параметр, переданый таким способом, нельзя модифицировать. Присваивание такому параметру (или компоненте параметра) и передача его по сслыке запрещены. Структурный параметр, переданный по доступу, не копируется, что позволяет повысить эффективность процедуры и уменьшить требования к памяти.

Последний параметр процедуры может быть параметром-последовательностью. Такой параметр может быть передан по сслылке (SEQ VAR) или по доступу, если VAR отсутствует. Такой формальный параметр внутри процедуры аналогичен гибкому массиву.

Параметр-последовательность типа T может быть передан целиком процедуре с формальным параметром-последовательностью типа T1, если тип T совместим с типом T1 и способ передачи у обоих параметров одинаковый (оба по доступу или оба по ссылке).

Снято ограничение на поэлементное использование гибких массивов. Разрешены присваивания гибких массивов (и параметра-последовательности) целиком. Компилятор вставляет динамические проверки того, что длина массива в левой части равна длине массива в правой части оператора присваивания. Для литерных массивов размеры сравниваются не на равенство, а на больше или равно.


<10.2> Стандартные процедуры

В <10.2.1*> описываются введенные стандартные процедуры, в <10.2.2*> - настраиваиваемые стандартные процедуры работы с памятью, в данном пункте описываются изменения в существующих стандартных процедурах.

1. У процедуры HALT разрешен необязательный параметр типа INTEGER, значение которого системно-зависимо. Вызов процедуры HALT с параметром означает аварийное завершение задачи и может приводить к действиям, облегчающим отладку. Вызов процедуры HALT без параметра означает нормальное завершение задачи, неотличимое от выполнения возврата из нулевой процедуры головного модуля.

2. Процедура HIGH может применяться к массивам, гибким массивам и динмассивам. Результат имеет тип INTEGER и является константой, если операнд имеет тип массив.

3. Процедура ORD возвращает результат типа INTEGER.

4. Параметр процедуры SIZE может быть произвольным обозначением. Результат имеет тип INTEGER и является константой, если операнд не является гибким массивом или динмассивом. Размер выдается в единицах адресации (для процессоров Кронос - в словах).

5. Отсутствует процедура VAL.


<10.2.1*> Дополнительные стандартные процедуры

1. ASSERT(BOOLEAN [ "," INTEGER]);

Если значение первого операнда - истина, то вызов эквивалентен пустому оператору, иначе исполнение программы завершается. Второй параметр может отсутствовать, а если он есть, то это некоторая дополнительная информация о причине завершения. Например:

ASSERT(adr=NIL,НетПамяти);

где НетПамяти - это константа ОС.

2. BITS( Обозначение | Тип )

Аналогична процедуре SIZE, но выдает размер объекта или типа в битах.

3. BYTES( Обозначение | Тип )

Аналогична процедуре SIZE, но выдает размер объекта или типа в байтах.

4. LEN(VAR x: ЛюбойМассив)

Выдает число элементов массива, гибкого массива или динмассива. Результат имеет тип INTEGER и является константой, если операнд имеет тип массив.

5. REF(VAR x: T , T2 )

T - произвольный тип, T2 должен быть типом указателя на тип T (T2 = POINTER TO T). Выдает значение типа T2, являющееся адресом объекта x.


<10.2.2*> Настраиваемые стандартные процедуры

Стандартные процедуры работы с памятью выполняют операции размещения, освобождения указателей и динмассивов и изменения размера динмассивов. В описании процедуры используются обозначения P - для произвольного указателя, D - для произвольного динмассива. Перед использованием этих стандартных процедур необходимо настроить их с помощью описателя динамической поддержки (см. <14.1*>). При настройке задается соответствие между стандартной процедурой и процедурой, определенной пользователем, которая и будет вызываться при вызове стандартной процедуры.

1. NEW(P)

Выделение памяти для указателя размером SIZE(P). Должна быть настроена процедура NEW.

NEW(D [,N])

Размещение памяти для динмассива с числом элементов N. После вызова верхняя граница динмассива равна N-1. Вызов NEW(D) эквивалентен вызову NEW(D,0). При этом вызов NEW(D,N) является сокращением следующего действия:

D^.HIGH:=-1; RESIZE(D,N).

Должна быть настроена процедура RESIZE.

2. DISPOSE(P)

Освобождение памяти, занятой указателем. Должна быть настроена процедура DISPOSE.

DISPOSE(D)

Освобождение памяти, занятой динмассивом. При этом вызов DISPOSE(D) является сокращением вызова RESIZE(D,0). Должна быть настроена процедура RESIZE.

3. RESIZE(D,N)

Изменение размера динмассива. При N=0 эквивалентна вызову процедуры DISPOSE. После вызова верхняя граница динмассива равна N-1. Сохраняет значения элементов динмассива для индексов 0..min(H0,N-1), где H0 - верхняя граница динмассива до вызова процедуры RESIZE. Должна быть настроена процедура RESIZE.


<11> Модули

Приоритет для локальных модулей не реализован.

$ Импорт = [ FROM Идентификатор ] IMPORT СписИмпорта.

$ СписИмпорта = ИмпортСекция { "," ИмпортСекция }.

$ ИмпортСекция = [ Идентификатор ":" ] Идентификатор.

В списке импорта разрешено переименование импортируемых идентификаторов (аналогично [3]), что позволяет использовать внутри модуля короткие (удобные) названия для этих идентификаторов. Запись

IMPORT in: OUT;

означает, что объект, видимый вне модуля под именем OUT, должен использоваться внутри модуля по имени in.

Пример:

IMPORT io: StdIO;

io.Write('A'); -- обозначает StdIO.Write('A')


<12> Системно-зависимые возможности

Модуль SYSTEM содержит типы WORD, ADDRESS и стандартную процедуру ADR.


<13> Процессы

Операции создания процессов, обработки прерываний и переключения процессов реализованы в библиотечных модулях. Поэтому средства, описываемые в данном пункте Сообщения, не реализованы.


<14> Единицы компиляции

$ Определение = CONST { ОписаниеКонстанты ";" } |

$               TYPE { Идентификатор [ "=" Тип ] ";" } |

$               VAR { ОписаниеПеременной ";" } |

$               VAL { ОписаниеПеременной ";" } |

$               ЗаголовокПроцедуры ";".

В определяющем модуле можно описывать переменные, которые в других модулях можно только читать (нельзя модифицировать). Такие переменные называются переменными только для чтения (read only, или VAL-переменные). В реализующем модуле, который соответствует данному определяющему, использование такой переменной не отличается от использования обычных переменных.

В заголовке программого и реализующего модуля допускается указание "приоритета". Приоритет модуля - это целое число, которое записывается в порожденный компилятором кодофайл и используется исполняющей системой в соответствии со стандартом ОС (см. 5.1.4).

$ ПрограммныйМодуль = MODULE Идентификатор

$     [ Приоритет ] ";" { Импорт }

$     { Описание | ДинПоддержка }

$     [ BEGIN ПослОператоров ] END Идентификатор ".".

Среди описаний в программном и реализующем модулях разрешается использование определения динамической поддержки (см. <14.1*>).


<14.1*> Динамическая поддержка

$ ДинПоддержка = WITH Идентификатор

$      ( ":" КвалИдент | "(" СписСоответствий ")" ).

$ СписСоответствий = Соответствие { ";" Соответствие }.

$ Соответствие = Идентификатор ":" КвалИдент.

Понятие динамической поддержки определяет набор процедур (и, может быть, других объектов), которые компилятор будет использовать при генерации некоторых конструкций языка. При этом указывается стандартный идентификатор - имя раздела, а затем следует указание соответствий или имя модуля, содержащего необходимые объекты со стандартными именами.

В текущей версии языка (и компилятора) определен один раздел динамической поддержки - STORAGE, содержащий настраиваемые операции работы с памятью.


<14.1*> Динамическая поддержка работы с памятью

Три стандартные процедуры (см. <10.2.2*>) требуют определения динамической поддержки для раздела STORAGE.


NEW: Настраивается процедурой типа

PROCEDURE (VAR ADDRESS, INTEGER);

,где второй параметр определяет размер требуемой памяти в словах.


DISPOSE: Настраивается процедурой типа

PROCEDURE (VAR ADDRESS, INTEGER);

После вызова первый параметр равен NIL.


RESIZE: Настраивается процедурой типа

PROCEDURE (VAR ADDRESS,

           VAR INTEGER,

               INTEGER,

               INTEGER);

Первый параметр - адрес массива, второй - его верхняя граница, третий - требуемая длина, четвертый - размер элемента массива в байтах.

При сокращенной настройке указывается имя модуля, который содержит процедуры с именами: ALLOCATE, DEALLOCATE и REALLOCATE. Эти процедуры должны иметь типы, описанные выше. Часть процедур может отсутствовать, при этом соответствующая стандартная процедура будет не определена. Все операции над динмассивами реализуются компилятором посредством процедуры REALLOCATE. Соответственно, программа, использующая динмассивы, должна содержать настройку для процедуры RESIZE.

Пример:

WITH STORAGE (NEW: Storage.ALLOCATE; DISPOSE: dealloc);

WITH STORAGE: Heap;

5.1.2.3. Синтаксис входного языка

Приведен синтакис Модулы-2 из [5] дополненый расширениями языка. Символом '*' помечены измененные и добавленные правила.

   1    Идентификатор = Буква {Буква | Цифра}.

   2    Число = Целое | Действительное.

   3    Целое = Цифра { Цифра } | ВосьмеричнаяЦифра

   4            { ВосмеричнаяЦифра } "B" |

   5            Цифра { ШестнадцатеричнаяЦифра } "H" |

*  5.1          ПредставлениеЛитеры.

*  5.2  ПредставлениеЛитеры =

*  5.3                  ВосьмеричнаяЦифра { ВосмеричнаяЦифра } "C".

   6    Действительное = Цифра { Цифра }

   7                     "." { Цифра } [Порядок].

   8    Порядок = "E" [ "+" | "-" ] Цифра { Цифра }.

   9    ШестандатиричнаяЦифра =

  10                    Цифра |"A"|"B"|"C"|"D"|"E"|"F".

  11    Цифра = ВосьмеричнаяЦифра |"8"|"9".

  12    ВосьмеричнаяЦифра =

  13                    "0"|"1"|"2"|"3"|"4"|"5"|"6"|"7".

* 14    Цепочка = Строка { Строка | ПредставлениеЛитеры }.

* 14.1  Строка = "'" { Литера } "'" | '"' { Литера } '"'.

  15    КвалИдент = Идентификатор { "." Идентификатор }.

  16    ОписаниеКонстанты =

  17                    Идентификатор "=" КонстВыражение.

  18    КонстВыражение = Выражение.

  19    ОписаниеТипа = Идентификатор "=" Тип.

  20    Тип = ПростойТип | ТипМассив | ТипЗапись

  21          | ТипМножество | ТипУказатель | ТипПроцедура

* 21.1        | ТипДинМассив.

  22    ПростойТип = КвалИдент | Перечисление

  23                 | ТипДиапазон.

  24    Перечисление = "(" СписИдент ")".

  25    СписИдент = Идентификатор { "," Идентификатор }.

* 26    ТипДиапазон =

  27                "[" КонстВыражение ".." КонстВыражение "]".

  28    ТипМассив = ARRAY ПростойТип { "," ПростойТип }

  29                OF Тип.

  30    ТипЗапись = RECORD ПослСписковКомпонент END.

  31    ПослСписковКомпонент = СписокКомпонент

  32                           { ";" СписокКомпонент }.

  33    СписокКомпонент ╜ [ СписИдент ":" Тип |

  34                      CASE [Идентификатор] ":" КвалИдент OF Вариант

  35                      { "|" Вариант}

  36                      [ ELSE ПослСписковКомпонент ] END ].

  37    Вариант = [ СписокМетокВарианта ":" ]

  38              ПослСписковКомпонент.

  39    СписокМетокВарианта = МеткиВарианта

  40                          { "," МеткиВарианта }.

  41    МеткиВарианта = КонстВыражение [ ".." КонстВыражение ].

  42    ТипМножество = SET OF ПростойТип.

  43    ТипУказатель = POINTER TO Тип.

  44    ТипПроцедура ╜ PROCEDURE [ СписокФормТипов ].

  45    СписокФормТипов = "(" [[VAR] ФормТип

* 46                      { "," [VAR] ФормТип } ] [ SEQ [VAR] КвалИдент ] ")"

* 46.1                    [ ":" КвалИдент ].

* 46.2  ТипДинМассив = DYNARR OF Тип.

  47    ОписаниеПеременной = СписИдент ":" Тип.

  48    Обозначение = КвалИдент { "." Идентификатор |

  49                  "[" СписВыражений "]" | "^" }.

  50    СписВыражений = Выражение { "," Выражение }.

  51    Выражение = ПростоеВыражение

  52                [ Отношение ПростоеВыражение ].

  53    Отношение = "=" | "#" | "<" | "<="

  54                | ">" | ">=" | IN.

  55    ПростоеВыражение = [ "+" | "-" ] Слагаемое

  56                       { ОперацияТипаСложения Слагаемое}.

  57    ОперацияТипаСложения = "+" | "-" | OR.

  58    Слагаемое = Множитель

  59                { ОперацияТипаУмножения Множитель}.

  60    ОперацияТипаУмножения = "*" | "/" | DIV

  61                            | MOD | AND

* 61.1                          | REM | "<<" | ">>".

  62    Множитель = Число | Цепочка | Множество |

  63                Обозначение [ ФактическиеПараметры ] |

  64                "(" Выражение ")" | NOT Множитель |

* 64.1              Конструктор.

  65    Множество = [ КвалИдент ]

  66                "{" [ Элемент { "," Элемент } ] "}".

  67    Элемент = Выражение [ ".." Выражение ].

* 67.1  Конструктор = ARRAY OF КвалИдент

* 67.2                "{" КонстВыражение { "," КонстВыражение } "}".

  68    ФактическиеПараметры = "(" [ СписВыражений ] ")".

  69    Оператор = [ Присваивание | ВызовПроцедуры |

  70               УсловныйОператор | ОператорВыбора |

  71               ЦиклПока | ЦиклДо | БезусловныйЦикл |

  72               ЦиклСШагом | ОператорПрисоединения |

  73               EXIT | RETURN [ Выражение ]].

  74    Присваивание = Обозначение ":=" Выражение.

  75    ВызовПроцедуры =

  76                Обозначение [ ФактическиеПараметры ].

  77    ПослОператоров = Оператор { ";" Оператор }.

  78    УсловныйОператор = IF Выражение THEN ПослОператоров

  79                       { ELSIF Выражение THEN ПослОператоров }

  80                       [ ELSE ПослОператоров ] END.

  81    ОператорВыбора = CASE Выражение OF Альтернатива

  82                     { "|" Альтернатива } [ ELSE ПослОператоров ] END.

  83    Альтернатива = [ СписокМетокВырианта ":"

  84                   ПослОператоров ].

  85    ЦиклПока = WHILE Выражение DO ПослОператоров END.

  86    ЦиклДо = REPEAT ПослОператоров UNTIL Выражение.

  87    ЦиклСШагом = FOR Идентификатор ":="

  88                 Выражение TO Выражение

  89                 [ BY КонстВыражение ] DO ПослОператоров END.

  90    БезусловныйЦикл = LOOP ПослОператоров END.

  91    ОператорПрисоединения = WITH Обозначение

  92                            DO ПослОператоров END.

  93    ОписаниеПроцедуры = ЗаголовокПроцедуры ";"

* 94                        ( Блок | Код ) Идентификатор.

* 94.1  Код = CODE { КонстВыражение } END.

  95    ЗаголовокПроцедуры = PROCEDURE Идентификатор

  96                         [ ФормальныеПараметры ].

  97    Блок = { Описание } [ BEGIN ПослОператоров ] END.

  98    Описание = CONST { ОписаниеКонстанты ";" } |

  99               TYPE { ОписаниеТипа ";" } |

 100               VAR { ОписаниеПеременной ";" } |

 101               ОписаниеПроцедуры ";" | ОписаниеМодуля ";"

*101.1             ЗаголовокПроцедуры ";" FORWARD ";".

 102    ФормальныеПараметры =

 103                    "(" [ ФПСекция { ";" ФПСекция } ]

*103.1                  [ SEQ [ VAR ] Идентификатор ":" КвалИдент ] ")"

 103.2                  [ ":" КвалИдент ].

*104    ФПСекция = [ VAR | VAL ] СписИдент ":" ФормТип.

 105    ФормТип = [ ARRAY OF ] Квалидент.

*106    ОписаниеМодуля = MODULE Идентификатор

 107                     ";" { Импорт } [ Экспорт ] Блок Идентификатор.

 108    Приоритет = "[" КонстВыражение "]".

 109    Экспорт = EXPORT [ QUALIFIED ] СписИдент ";".

*110    Импорт = [ FROM Идентификатор ] IMPORT СписИмпорта.

*110.1  СписИмпорта = ИмпортСекция { "," ИмпортСекция }.

*110.2  ИмпортСекция = [ Идентификатор ":" Идентификатор ].

 111    МодульОпределений = DEFINITION MODULE Идентификатор

 112                        ";" { Импорт } { Определение } END

 113                        Идентификатор ".".

 114    Определение = CONST { ОписаниеКонстанты ";" } |

 115                  TYPE { Идентификатор [ "=" Тип ] ";" } |

 116                  VAR { ОписаниеПеременной ";" } |

*116.1                VAL { ОписаниеПеременной ";" } |

 117                  ЗаголовокПроцедуры ";".

 118    ПрограммныйМодуль = MODULE Идентификатор

 119                        [ Приоритет ] ";" { Импорт }

*119.1                      { Описание | ДинПоддержка }

 119.2                      [ BEGIN ПослОператоров ] END Идентификатор ".".

*119.3  ДинПоддержка = WITH Идентификатор

*119.4                 ( ":" КвалИдент | "(" СписСоответствий ")" ).

*119.5  СписСоответствий = Соответствие { ";" Соответствие }.

*119.5  Соответствие = Идентификатор ":" КвалИдент.

 120    ЕдиницаКомпиляции = МодульОпределения |

 121                        [ IMPLEMENTATION ] ПрограммныйМодуль.

5.1.3. Еще раз о расширениях

В этом пункте делается попытка заранее ответить на следующие вопросы читателя касательно расширений:

  • Зачем нужны расщирения?
  • Кому это выгодно?
  • А как этим пользоваться?

Примеры использования расширений приводятся в 5.1.6.

5.1.3.1. Динмассивы

Динмассивы удобно использовать для хранения информации, размеры которой статически не известны. Необходимо понимать двойственность структуры динмассива (или тройственность?).

Динмассив представляет собой пару дескриптор + массив. Значение типа динмассив занимает два слова под деcкриптор и сколько надо слов под тело массива. Если динмассив является компонентой некоторой структуры (записи, массива, динмассива), то при копировании структуры будет скопирован только дескриптор, например:

VAR a,b: RECORD

           dyn: DYNARR OF INTEGER;

           ptr: POINTER TO INTEGER;

         END;

  a:=b;

После этого присваивания поля a.dyn и b.dyn будут ссылаться на одно и то же тело массива. Ситуация аналогична ситуации для указателей: поля a.ptr и b.ptr указывают в одно место памяти.

Дескриптор динмассива (или паспорт) представляет собой запись из двух полей:

RECORD

  ADR : SYSTEM.ADDRESS;

  HIGH: INTEGER;

END;

Поле ADR содержит адрес массива, а поле HIGH - значение его верхней границы.

Запись d^ (где d - динмассив) обозначает доступ к дескриптору, d^.ADR и d^.HIGH обозначает соответствующие поля дескриптора.

Динмассив ведет себя как гибкий массив в следующих случаях:

  • в индексации d[i];
  • в вызовах стандартных процедур HIGH, LEN, SIZE, BYTES, BITS;
  • при передаче параметра, если формальный параметр является гибким массивом;
  • при передаче параметра, если тип формального параметра - последовательность слов.

В следующем примере приводятся почти все случаи использования динмассивов:

PROCEDURE p1( s: ARRAY OF CHAR); END p1;

PROCEDURE p2(VAR s: ARRAY OF CHAR); END p2;

PROCEDURE p3( s: STRING); END p3;

PROCEDURE p4(VAR s: STRING); END p4;

PROCEDURE p5(SEQ x: SYSTEM.WORD); END p5;


VAR a,b: STRING;

    adr: ADDRESS;

    n:   INTEGER;


  a:=b;          -- копирование строк;

  a^:=b^;        -- копирование дескрипторов;

  a^.HIGH:=1;    -- изменение верхней границы;

  a^.ADR:=NIL;   -- изменение адреса массива;

  n:=SIZE(a^);   -- n = 2;

  n:=SIZE(a);    -- n = размер массива;

  adr:=ADR(a);   -- adr = a^.ADR;

  adr:=ADR(a^);  -- adr = адрес дескриптора;

  p1(a);         -- передача массива по значению;

  p2(a);         -- передача массива по ссылке;

  p3(a);         -- передача дескриптора по значению;

  p4(a);         -- передача дескриптора по ссылке;

  p5(a);         -- передача адреса массива;

  p5(a^);        -- передача адреса дескриптора.

Стандартные процедуры NEW, DISPOSE, RESIZE (см. 5.1.2) реализуют операции выделения памяти динмассиву, возвращения памяти и изменения размера динмассива. При этом практически никогда не появляется необходимость обращаться к дескриптору динмассива.

5.1.3.2. Процедуры с переменным числом параметров

Последним параметром у процедуры может быть параметp-последовательность. При вызове процедуры вместо такого формального параметра может быть подставлена последовательность фактических параметров (в том числе и пустая). Внутри процедуры такой параметр аналогичен параметру типа ARRAY OF T. Если параметр передается не по ссылке, то присваивание элементам массива-параметра запрещено, то есть подразумевается передача по доступу.

Пример:

PROCEDURE Min(SEQ x: INTEGER): INTEGER;

  VAR min,i: INTEGER;

BEGIN

  ASSERT(HIGH(x)>=0);

  min:=x[0];

  FOR i:=1 TO HIGH(x) DO

    IF x[i]<min THEN min:=x[i] END

  END;

  RETURN min

END Min;

........

i:=Min(1,2,3,4);

Параметр-последовательность типа T может быть передан целиком процедуре с формальным параметром-последовательностью типа T1, если тип T совместим с типом T1 и способ передачи у обоих параметров одинаковый (оба по значению или оба по ссылке).

PROCEDURE p(SEQ x: INTEGER);

.......

  i:=Min(x);

.......

Замечание:  если тип последовательности WORD, то фактическими параметрами могут быть не только однословные объекты, но и структуры. В этом случае передается только их адрес и копирования не происходит.

Введение в язык процедур с переменным числом параметров оказало большое влияние на библиотеки ввода/вывода. Вместо большого количества процедур вывода, таких, как WriteChar, WriteInteger, WriteReal и т.д., что характерно для других реализаций Модулы-2, наши библиотеки содержат процедуры форматного вывода, которые позволяют формировать выходной поток существенно удобней.

PROCEDURE print(format: ARRAY OF CHAR; SEQ x: SYSTEM.WORD);

Строка format содержит описание формата вывода (в стандарте Unix SYSTEM V).

Пример:

print('factorial(%d)=%d\n',n,factorial(n));

Если n=5 (и процедура с именем factorial действительно реализует факториал), то результатом будет:

factorial(5)=120

5.1.3.3. Динамическая поддержка, определяемая пользователем

Одним из преимуществ Модулы-2 как языка программирования систем (в том числе ОС и систем реального времени) является отсутсвие необходимости в динамической поддержке (Run Time Support - RTS). Отсутствие RTS отвязывает компилятор от реальной системы, в которой будет работать полученный им код.

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

  • исключительные ситуации;
  • аккуратные операции над кучей (NEW, DISPOSE, RESIZE);
  • операторы параллельности;
  • и так далее...

Реализация всего этого в компиляторе требует некоторых знаний о системе, а в качестве библиотеки их невозможно реализовать без потери надежности (или вообще невозможно реализовать, например RESIZE(любой_динмассив,новая_длина)).

Рассуждая таким образом, можно прийти к выводу о необходимости совместной реализации таких вещей в компиляторе и бибилиотеке. Компиляторная часть должна обеспечить надежность и контроль, библиотечная же часть может использовать конкретную систему. Существенным требованием к такой реализации должно оставаться отсутствие обязательного RTS, то есть: те модули, которые не пользуются новыми языковыми конструкциями, не должны изменяться от введения таковых.

В текущей версии с помощью динамической поддержки реализованы операции работы с памятью.

5.1.4. Режимы компиляции

5.1.4.1. Прагматы

Текст Модула-программы может содержать управляющие последовательности, которые мы будем называть прагматами. Прагматы служат для изменения режима компиляции внутри текста программы.

прагмат   = "(*$" директива "*)".

директива = { опция "+" | "-" | "!" | "<" | ">" }

опция     = 'A'|'F'|'I'|'N'|'R'|'T'

Каждая опция означает действие, которое может быть включено (отключено) управляющей последовательностью. Смысл опций и их начальное состояние приводится в таблице.

N Опция Значение По умолчанию
1 A показ всех ошибок off
2 F аварийное окончание компиляции при первой встреченной ошибке off
3 I инициализация переменных типа указатель значением NIL; типа динмассив - парой (NIL,-1) on
4 N проверка на NIL для указателей и динмассивов. При включении автоматически включает опцию I off
5 R контроль выхода за границы отрезка on
6 T контроль индексов массивов on

Остальные директивы определяют операции над стеком режимов трансляции.

  1. < - запомнить текущее состояние опций в стеке;
  2. > - восстановить последнее состояние опций из стека;
  3. ! - счеркнуть весь стек режимов и перейти в исходное состояние.

Начальное состояние режимов компиляции может быть задано при запуске компилятора средствами системы программирования (см. 5.2).

5.1.4.2. Версия системы команд

Компилятор может порождать код модуля, используя три версии системы команд:

  • базовая версия (версия 0). Код базовой версии может выполняться на всех моделях процессоров семейства Кронос;
  • расширенная версия (версия 1). Компилятор использует набор дополнительных команд, реализованных на процессорах 2.5 и 2.6WS.
  • версия рабочей станции (версия 2). Компилятор использует набор дополнительных команд, реализованных на процессоре 2.6WS.

Номер версии системы команд может быть задан при запуске компилятора (см. 5.1.2). Если номер версии не указан, то компилятор порождает код для того процессора, на котором работает.

За более подробными сведениями о версиях системы команд отсылаем читателя к книге "Архитектура процессоров семейства КРОНОС".

5.1.4.3. Приоритет модуля

В заголовке модуля может быть указан приоритет модуля - целое число, интерпретация которого зависит от ОС. В текущей версии определены следующие приоритеты:

  • 0 - обычный модуль;
  • 1 - уникальный модуль.

Данные уникального модуля не дублируются, если этот модуль используется несколькими задачами (подробнее см. в документации по ОС).

5.1.5. Стиль программирования

Этот пункт содержит необязательные рекомендации по разработке программ на языке Модула-2. Набор рекомендаций включает в себя правила расположения текста, способ комментирования, правила выбора идентификаторов и использования языковых конструкций. Эти правила были выработаны группой Кронос для внутреннего использования. Совокупность правил определяет "стиль" программирования. Использование единого стиля упрощает совместную работу над проектами и является простым способом увеличения читабельности программ.

Особенно важно придерживаться единого стиля при оформлении общеполезных библиотек.

5.1.5.1. Расположение текста

Отступы используются для выделения структуры программы. Ширина каждого отступа 2 символа (пробела). Следующий пример иллюстрируют рекомендуемую форму некоторых конструкций Модулы-2:

MODULE Example; (* 29-Mar-90. (c) KRONOS *)


CONST

  one = 1;

  two = 2;


TYPE

  Mode = (root,path,leaf);

  node_ptr = POINTER TO node_rec;

  node_rec = RECORD

               mode : Mode;

               info : INTEGER;

               left : node_ptr;

               right: node_ptr;

             END;


VAR

  info: INTEGER;

  root: node_ptr;


PROCEDURE p(VAR n: node_ptr);

  VAR i: INTEGER;

BEGIN

  i:=0;

  n^.info:=info; INC(info);

  IF node^.left =NIL THEN node^.left :=n

  ELSIF node^.right=NIL THEN node^.right:=n

  ELSE DEC(info);

  END;

END p;


BEGIN

  CASE node^.mode OF

    |root:

    |path:

  ELSE ASSERT(FALSE);

  END;

END Example;

Если языковая конструкция достаточно мала, то рекомендуется размещать ее на одной строке текста.

IF a>b THEN min:=b ELSE min:=a END;

FOR i:=0 TO HIGH(a) DO a[i]:=0 END;

Несколько простых операторов могут быть размещены на одной строке. Рекомендуется размещать на одной строке несколько "сильно" связанных операторов присваивания. Например, операторы, ввязывающие элемент в односвязный список:

n^.next:=head; head:=n;

5.1.5.2. Комментарии

Используются традиционные комментарии для оформления больших текстов. Комментарии в стиле Ады употребляются в следующих случаях:

  • комментирование данной строки;
  • пометка об изменении строки;
  • указание ключевого места в тексте;
  • выделение заголовка раздела.

Примеры:

TYPE

  node_rec = RECORD

               mode : Mode; -- вид узла

               left : node_ptr; -- левый сын

               right: node_ptr; -- правый сын

             END;


----------------------- NEW SECTION -------------------------

                      ---------------


PROCEDURE joke(a,b: INTEGER): BOOLEAN;

  VAR min: INTEGER;

BEGIN

  IF a>b THEN min:=a ELSE min:=b END; -- modified 29-Mar-90

  IF min<0 THEN RETURN TRUE END;

                       ----

  RETURN FALSE

END joke;

5.1.5.3. Именование

Как правило, все имена записываются только строчными буквами. Если имя состоит из нескольких слов, могут быть использованы два способа именования:

local_var    localVar

node_ptr     nodePtr

Рекомендуется использование подчерка.

Основные требования предъявляются к именам в определяющих модулях. Рекомендуется следующий стиль:

  • процедуры в разных библиотеках, реализующие аналогичные операции, называются одинаково (Terminal.print, StdIO.print);
  • существуют устойчивые пары имен, не надо смешивать имена из разных пар; например:

put        get

read       write

new        dispose

insert     delete

install    remove


  • имена скрытых типов состоят только из заглавных букв;
  • имена типов указателей и записей оканчиваются добавкой _ptr и _rec соответственно;

5.1.5.4. Использование языковых конструкций

В определяющих модулях не рекомендуется использование типа диапазона и типа перечисления, если набор констант может измениться. Так, не стоит заводить перечислимый тип OS_errors, но нет ничего плохого в том, чтобы определить тип

Week =(Monday,...,Sunday)

, так как это от Бога.

В реализующих модулях не рекомендуется использование локальных модулей (от себя все равно не скроешь) и расквалифицирующего импорта (FROM IMPORT). Напротив, очень рекомендуется использование переименования при импорте. Это существенно увеличивает читабельность текста (всегда видно Что, Где и Почем).

В процедурах типа "open", "new", которые создают новый объект, рекомендуется делать возвращаемый объект первым параметром. Желательно воздерживаться от подряд идущих параметров одного типа, особенно - основного типа. Так, лучше писать:

PROCEDURE (INTEGER,BOOLEAN,INTEGER)

вместо

PROCEDURE (INTEGER,INTEGER,BOOLEAN).

5.1.5.5. Оформление определяющего модуля библиотек

Рекомендуется следующий вид библиотеки:

DEFINITION MODULE имя; (* <автор> <время написания> *)

ТЕКСТ НА МОДУЛЕ-2

(*************************************************************

Развернутый комментарий, состоящий из описания принципов использования библиотеки и пояснения к каждой процедуре, содержащий описания параметров процедуры, диапазон допустимых значений и реакцию на исключительные ситуации.

*************************************************************)

END имя.

5.1.6. Примеры программ

5.1.6.1. Рассмотрим простой модуль работы с текстом для текстового редактора. Модуль реализует операции сохранения строки текста, возвращения строки основному модуля редактора, удаления строк и так далее. В примере используется библиотека работы со строками.

DEFINITION MODULE edText; (* Ned 29-Mar-90. (c) KRONOS *)


PROCEDURE get(lineno: INTEGER; VAR s: ARRAY OF CHAR);

(* Выдает содержимое строки по ее номеру. *)


PROCEDURE put(lineno: INTEGER; s: ARRAY OF CHAR);

(* Запоминает содержимое строки *)


PROCEDURE delete(lineno,lines: INTEGER);

(* Удаляет lines строк, начиная с lineno *)


PROCEDURE insert(lineno,lines: INTEGER);

(* Вставляет lines пустых строк перед строкой lineno *)


END edText.


IMPLEMENTATION MODULE edText; (* Ned 29-Mar-90. (c) KRONOS *)


IMPORT str: Strings; (*0*)

IMPORT      Heap;


WITH STORAGE: Heap; (*1*)


TYPE TEXT = DYNARR OF STRING;


VAR text: TEXT;


PROCEDURE get(lineno: INTEGER; VAR s: ARRAY OF CHAR);

BEGIN

  IF lineno>HIGH(text) THEN s:=''

  ELSE

    str.copy(s,text[lineno]); (*2*)

  END;

END get;


PROCEDURE put(lineno: INTEGER; VAL s: ARRAY OF CHAR);

  VAR x: STRING;

BEGIN

  IF lineno>HIGH(text) THEN RESIZE(text,lineno+16) END; (*3*)

  NEW(x,str.len(s)+1); (*4*)

  str.copy(x,s);

END put;


PROCEDURE delete(lineno,lines: INTEGER);

  VAR i,n: INTEGER;

BEGIN

  IF lineno+lines>=LEN(text) THEN

    FOR i:=lineno TO HIGH(text) DO DISPOSE(text[i]) END;

  ELSE

    FOR i:=lineno TO lineno+lines-1 DO DISPOSE(text[i]) END;

    n:=lineno+lines;

    FOR i:=lineno TO HIGH(text) DO

      IF n<=HIGH(text) THEN text[i]^:=text[n]^; (*5*)

      ELSE NEW(text[i]);

      END;

      INC(n);

    END;

  END;

END delete;


BEGIN

  NEW(text);

END edText.

Примечания:

(*0*) Импорт модуля Strings с переименованием.
(*1*) Определение настраиваемых стандартных процедур работы с памятью.
(*2*) Копирование строк.
(*3*) Увеличение числа элементов в динмассиве text.
(*4*) Выделение памяти для строки. Число элементов в строке вычисляется как число элементов параметра плюс один (для хранения символа конца строки - 0c).
(*5*) Копирование дескриптора строки.


Глава 5.2. Использование компилятора

Компилятор "mx" состоит из ядра и набора утилит. Ядро является конструтором для создания конечного продукта, каждая утилита реализует некоторый способ использования компилятора. В базовый набор постановки системы входит несколько таких утилит: пакетный компилятор "mx", турбо-компилятор "turbo2x" и утилита поддержки разработки "pm" (project manager).

Использование пакетного компилятора описывается в 5.2.2, турбо-компилятора - в 5.2.3. Все эти утилиты одинаковым образом настраиваются на окружение задачи (environment). Описание окружения приводится в 5.2.7. В 5.2.1 описываются соглашения об именовании файлов. Способы разработки новых компилирующих утилит описываются в 5.3.2.

Содержание этого раздела существенно зависит от операционной системы, в которой работает компилятор (см. описание утилит ОС Excelsior).

5.2.1. Имена модулей и файлов

При работе на Модуле-2 программисту приходится иметь дело с пятью сущностями:

  • определяющие модули;
  • реализующие (и программные) модули;
  • симфайлы;
  • реффайлы;
  • кодофайлы.

Определяющий модуль содержит информацию о способе использования некоторых объектов (интерфейс), реализующий модуль определяет реализацию операций (функций). Программный модуль удобно рассматривать как реализующий модуль с пустым интерфейсом.

Для модуля с именем M рекомендуется текст определяющего модуля хранить в файле с именем M.d, а текст реализующего модуля в файле с именем M.m.

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

При компиляции реализующего модуля компилятор порождает кодофайл (исполняемый образ модуля) и реффайл, который содержит информацию, необходимую отладчикам. Для модуля с именем M компилятор записывает кодофайл в файл с именем M.cod, а реффайл в файл с именем M.ref.

По умолчанию все файлы, которые порождает компилятор, записываются на текущую директорию (см. 5.2.7, если есть желание записывать файлы в другие места).

5.2.2. Пакетный режим

Использование утилиты "mx":

mx { имя_файла } [ дополнительные_аргументы ]

Компилятор последовательно транслирует файлы, имена которых заданы в списке параметров утилиты. Если имя файла не содержит расширителя, то к имени добавляется постфикс ".m".

В начале компиляции на терминал выдается строка, содержащая имя и версию компилятора и имя компилируемого файла:

Modula X v0.7 /21-Mar-90/ [2] "a.m"

В квадратных скобках указана модель процессора.

При обнаружении ошибок компиляции на стандартный вывод выводится сообщение об ошибке, состоящее из пояснения типа ошибки и строки текста, в которой обнаружена ошибка. Позиция ошибки в строке помечается символом "$". Заметим, что символ "$" выставлен после ошибочной лексемы. Например:

Невидимый объект -- "aaaa"

5: i:=aaaa$;

Компиляция модуля прекращается, если число ошибок больше некоторого предела (см. 5.1.4).

После завершения компиляции модуля на терминал выдается сообщение, содержащее информацию о числе строк, числе ошибок и времени, затраченном на компиляцию:

errors: 1 lines 7 time 01cpu + 01io

Если ошибки не обнаружены, то сообщение содержит информацию о числе строк, времени компиляции, а также имя порожденного файла и размер кода (только при компиляции реализующих модулей).

lines 7 time 01cpu + 01io "a.cod" 1 words

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

files 2 lines 14 time cpu00:01 io00:01 speed 840 l/m 2 words

Если компилятор запущен открепленно, то строки, содержащие имя компилятора и информацию о модулях, не выдаются.

5.2.3. Турбо-компилятор

Турбо-компилятор представляет собой утилиту, которая компилирует текст модуля, находящегося в буфере редактора. При этом полученные в результате компиляции симфайл, кодофайл и реффайл записываются на диск.

Турбо-компилятор запускается из редактора (см. "Утилиты ОС Excelsior", раздел 'ex') нажатием клавиш F0 и 'm'.

Если в тексте при компиляции обнаружены ошибки, компилятор сообщает о них последовательно. Сообщение о характере ошибки при этом появляется в служебной (информационной) строке, а курсор позиционируется в том месте текста модуля (после той лексемы), где произошла ошибка.

Переход к следующей ошибке осуществляется нажатием клавиш F0 и '+'.

После того, как исправлены все ошибки, можно запустить отлаживаемую программу и, убедившись в правильности ее работы, записать текст на диск. Таким образом, время на исполнение стандартного отладочного цикла редактирование-компиляция- запуск может быть заметно сокращено.

5.2.4. Перечень сообщений компилятора

Приводим сообщения компилятора с комментариями там, где они требуются.

5.2.4.1. Сообщения типа "Слишком много"

1) Переполнена хеш-таблица (слишком много имен)

Текущая реализация не умеет работать с таким количеством переменных, процедур и других именованных объектов.

2) Переполнение стека выражений!

Выражение не удалось вычислить на стеке в 7 элементов.

3) Слишком сложное условное выражение

Много написано между условными операторами. Аппаратное ограничение. Не бывает.

4) Слишком большой размер типа

Размер типа превышает maxInt.

5) Слишком много параметров

Параметров у процедуры больше, чем позволяет текущая реализация (>256).

6) Ограничение транслятора: слишком много

Процедур или переменных больше, чем допускает текущая версия.

7) Размах меток оператора выбора > 256

5.2.4.2. Сообщения типа "Ожидалось"

1) Неожиданный конец исходного текста!

Ожидалось "END имя_модуля".

2) Ожидался идентификатор

3) Должно быть имя блока

Должно быть имя процедуры или модуля.

4) Отсутствует 'h' после шестнадцатеричного

5) Незакрытая или слишком длинная строка!

В конце строки текста не стоит ".

6) Ожидался символ

Например, ";" или ")".

7) Ожидалось константное выражение

Например, границы массива.

8) Ожидался оператор

Как правило, вызвано мусором в тексте модуля.

9) Код записывается байтами! [0..0FFh]

Ошибка в кодовой процедуре.

10) Некорректная информация в симфайле

Начало было похоже на симфайл, а дальше...

11) Ошибка в заголовке симфайла

Мусор в содержимом симфайла.

12) Ошибка в конструкторе типа

13) Ошибка в заголовке модуля

Ошибка в самой первой строке программы.

14) Незакрытый комментарий, начавшийся в строке

5.2.4.3. Семантические ошибки

1) Невидимый объект

Объект не описан или не проимпортирован.

2) Повторно объявлен

Такой объект уже был описан.

3) Рекурсивное определение объекта

Например, CONST A=A+1.

5.2.4.4. Ошибки при работе с типами

1) Недопустимое преобразование типа

Можно преобразовывать только в тип той же длины.

2) Типы несовместимы

3) Должен быть тип указателя

4) Скрытый тип должен быть однословным и не литерным

5) Должен быть тип

Идентификатор не является типом.

6) Должен быть скалярный тип

7) Должен быть простой (1 слово) тип

5.2.4.5. Ошибки в выражениях

1) Неправильное константное выражение

Выражение правильное, но не константное.

2) Неправильное выражение

3) Переполнение (исчерпание) в константном выражении

Выдается, например, при делении на 0.

5.2.4.6. Вызовы процедур и функций

1) Вызов процедуры в выражении

Вместо функции вызвана процедура.

2) Вызов функции в позиции оператора

3) Неправильное число параметров

Лишние или недостающие параметры в процедуре.

4) Это не процедура

5.2.4.7. Ошибки в версиях

1) Некорректная версия симфайла

Что-то устарело.

2) Конфликт версий (по времени компиляции)

См. раздел о конфликте версий.

5.2.4.8. Встретилось неожиданно (не на месте)

1) Непонятный знак игнорируется

Например, $ или %, @.

2) Должен быть массив

Попытка проиндексировать не массив ([ после идентификатора).

3) Должна быть запись

Встретилась ".".

4) Ошибка в описаниях

Мусор до BEGIN или отсутствует BEGIN.

5) Недопустимо в определяющем модуле

Например, VAL-параметр или BEGIN.

6) EXIT вне LOOP'а

Скорее всего, лишний END внутри LOOP-цикла.

7) Такая метка уже была

Одинаковые метки в CASE-операторе.

8) Должен быть тип множества

Использование { не по делу.

9) RETURN можно писать только в процедуре

В теле модуля нельзя.

5.2.4.9. Прочие ошибки

1) Неправильный (неконстантный) или вырожденный отрезок

Отрезок в описании типа должен быть константным и правая граница больше левой.

2) Не реализовано

Не реализовано в компиляторе.

3) Должна быть переменная

Объект существует, но не является переменной.

4) Не обладает адресом

Выдается, например, при попытке присвоить значение константе.

5) Не обладает значением

Например, вложенная процедура.

6) Недопустимое использование формального типа

ARRAY OF можно использовать только при описании заголовка процедуры.

7) Переменная цикла должна быть локальной

Должна быть описана в минимальном объемлющем блоке.

8) Это не модуль

Попытка проимпортировать что-то другое (или из чего-то другого).

9) Выход за границы диапазона

Выход за границы массива или битсета.

10) Экспорт невозможен. Объект уже объявлен

11) Доступ к дескриптору только с ключом $U+ (unsafe)

12) Нереализованная процедура

Ошибка в FORWARD-описании.

13) Попытка подсунуть чужой симфайл

Симфайл не модуловский. Не бывает.

14) Повторный FORWARD

15) Недопустимое использование идентификатора модуля

Попытка проиндексировать имя модуля.

16) Неправильный синтаксис строки

Ошибка между "".

17) Разрешено только на уровне единицы компиляции

18) Недоступная RTS процедура

NEW, DISPOSE, RESIZE не определены.

19) Спецификатор VAL недопустим в описании процедурного типа

20) В CASE нужна хоть одна альтернатива

21) Переменная цикла не может быть VAR-параметром

22) Присваивание VAL-переменной (Только Для Чтения)

23) Разрешено только в определяющем модуле

Попытка описать скрытый тип или VAL-переменную.

24) Копирование динмассивов только с ключом $X+

Временно.

5.2.5. Порядок компиляции

Сначала компилируют определяющий модуль, если он есть, затем - реализующий и только потом программный.

Если программа состоит из нескольких модулей, то компилируют сначала все определяющие модули, а затем реализующие.

При компиляции необходимо следить за порядком импорта, а именно: чтобы скомпилировался модуль М, импортирующий какой-нибудь объект из модуля N, требуется симфайл N.sym. Из этого следует вывод, что определяющий модуль N должен быть скомпилирован раньше, чем определяющий модуль M. Кроме того, этот симфайл должен быть видим, т.е. лежать на директории, указанной в пути. Например, симфайлы всех библиотек принято хранить на директории /$v/sym, где $v - имя носителя.

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

5.2.6. О конфликте версий

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

Первое происходит в случаях, которые можно описать схемой:

 
A.d ---------> B.d
 \             /
  \           /
   \         /
    \       /
     \     /
      \   /
       V V
       C.%

Здесь стрелки изображают порядок импорта объектов, C.% - определяющий (C.d) либо реализующий (C.m) модуль.

Если сначала был скомпилирован модуль A.d, затем B.d, а потом по какой-то причине снова A.d, то при компиляции модуля C возникает конфликт версий симфайлов A.sym и B.sym.

Ситуация, в которой возникает конфликт версий кода при запуске задачи, такова: если модуль C импортирует объекты из модуля A, а порядок компиляции оказался следующим:

A.d C.m A.d A.m

, то при запуске задачи возникает конфликт версий кодофайлов C.cod и A.cod.

5.2.7. Среда компиляции

Среда компиляции состоит из набора строк, определяющих поиск симфайлов и файлов, содержащих исходный текст, директории на которые будут записаны симфайлы, реффайлы и кодофайлы и режимы компиляции. Компилятор при запуске настраивается на окружение:

"SYM" - определяет пути поиска симфайлов; состоит из имен директорий, разделенных пробелами;
"mxTEXT" - определяет пути поиска файлов исходных текстов; состоит из имен директорий, разделенных пробелами;
"mxOUT" - определяет имена директорий, на которые будут записываться выходные файлы компилятора; состоит из пар равенств вида образец = путь, разделенных символом ":". Если имя выходного файла не сопоставляется ни с одним из образцов, то файл будет записан на текущую директорию;
"mxERRLIM" - определяет макисмальное число ошибок компиляции;
"mxCPU" - определяет версию системы команд. Если окружение задачи не содержит строки "mxCPU", то компилятор порождает код для той модели процессора, на которой работает;
"mxFLAGS" - определяет режимы компиляции. Содержит последовательность пар символов, состоящих из литеры, определяющей режим компиляции, за которым следует символ "+" или "-". Символ "+" обозначает включение режима, "-" - выключение.

Строки с указанными именами могут быть заданы в окружении задачи (об окружении см. в разделе, посвященном пользовательской оболочке): или параметрами при запуске компилятора.

Примеры:

SYM       ". /sym /usr/sym"

mxTEXT    ". new_project"

mxOUT     "*.cod=my_bin: mx*.sym=/usr/sym"

mxFLAGS   "N+T-"

mxERRLIM  "8"

mxCPU     "2"



Глава 5.3. Реализация компилятора

5.3.1. Детали реализации

Рассматриваются тонкие места входного языка, которые не определены Сообщением [5]. Речь будет идти о совместимости типов.

5.3.1.1. Тип T1 совместим с типом T0, если истинно одно из следующих утверждений:

1) T1 описан, как T1=T0 (т.е. они идентичны);

2) T1 описан как диапазон T0;

3) T0 описан как диапазон T1;

4) T0 и T1 описаны как диапазоны одного и того же типа;

5) один из типов есть ADDRESS, а другой - тип INTEGER (или диапазон типа INTEGER) или тип указателя.

Заметим, что отношение совместимости симметрично.

5.3.1.2. Тип T1 обобщенно совместим с типом T0, если истинно одно из следующих утверждений:

1) типы T1 и T0 совместимы;

2) один из типов есть WORD, а другой - любой однословный тип;

3) T1 и T0 есть процедурный типы и они процедурно совместимы.

Отношение обобщенной совместимости симметрично.

5.3.1.3. Тип T1 совместим по присваиванию с типом T0, если истинно одно из следующих утверждений:

1) типы T1 и T0 обобщенно совместимы;

2) T1 и T0 есть типы массивов литер и длина (число элементов) массива T1 не меньше длины массива T2;

3) T1 есть массивов литер, а T2 константа типа CHAR;

4) T1 и T0 есть типы массивов, гибких массивов или динмассивов и хотя бы один из них является типов гибкого массива или динмассива и их базовые типы совместимы;

Отношение совместимости по присваиванию несимметрично.

Отличия от Cообщения [5]:

  • в утверждении 2 по сообщению правым операндом может быть только цепочка (т.е. строковая константа);
  • разрешены присваивания гибких массивов целиком; при исполнении проверяется, что размеры массивов равны, а для массивов литер, что число элементов левого операнды не меньше числа элементов правого операнда.

5.3.1.4. Тип T1 процедурно совместим с типом T0, если истинны ВСЕ следующие утверждения:

1) если T1 есть процедура-функция, то T2 тоже есть процедура-функция и типы результатов обобщенно совместимы;

2) число параметров у процедур одинаково;

3) соответствующие параметры имеют одинаковый способ передачи;

4) у параметров типа гибкий массив типы элементов обобщенно совместимы, типы остальных параметров обобщенно совместимы.

5.3.1.5. Передача параметров

Основное правило:

Тип параметра-переменной должен быть обобщенно совместимы с типом фактического параметра. Тип параметра-значения должен быть совместим по присваиванию с типом фактического параметра. Если тип формального параметра - гибкий массив слов, то тип фактического параметра должен быть массивом (любым), а типы элементов формального и фактического параметров должны быть совместимы.

Параметры-последовательности:

Вместо формального параметра-последовательности может быть подставлена последовательность параметров любой длины (возможно пустая) или один фактический параметр, являющийся формальным параметром-последовательностью.

В первом случае тип каждого фактического параметра должен быть обобщенно совместим или совместим по присваиванию с типом последовательности (в зависимости от способа передачи). Во втором случае способ передачи последовательностей должен совпадать, а типы быть обобщенно своместимы.

Исключения:

1) Если тип формального параметра - массив слов, то фактическим параметром может быть объект любого структурного типа той же длины.

2) Если тип формального параметра - гибкий массив слов, то фактическим параметром может быть объект любого структурного типа.

3) Если тип последовательности есть WORD, то типы фактических параметров могут быть любыми. При этом если тип фактического параметра есть тип записи, массива, гибкого массива или динмассива, то в качестве параметра будет передан адрес структуры.

5.3.2. Структура компилятора

5.3.2.1. Интерфейс компилятора

Тексты модулей приводятся в качестве примера и могут не полностью совпадать с библиотеками текущей версии ОС и компилятора.

Модуль coolDefs определяет представление операций ввода/вывода.

DEFINITION MODULE coolDefs; (* Ned 06-Jan-90. (c) KRONOS *)


IMPORT SYSTEM;


CONST -- виды модуля/файла

  def    = 0; -- определяющий модуль

  imp    = 1; -- реализующий модуль

  main   = 2; -- программный модуль

  text   = 3; -- текст

  sym_in = 4; -- входной симфайл

  sym_ou = 5; -- выходной симфайл

  ref    = 6; -- реффайл

  code   = 7; -- кодофайл


TYPE

  PRINT = PROCEDURE (ARRAY OF CHAR, SEQ SYSTEM.WORD);

  io_ptr = POINTER TO io_rec;

  io_rec = RECORD

             kind : INTEGER;            -- вид модуля/файла

             doio : PROCEDURE (io_ptr); -- операция в/в

             done : BOOLEAN;            -- результат операции

             print: PRINT;              -- для сообщений

             buf : STRING;              -- буфер в/в

             len : INTEGER;             -- длина в/в

             exts : SYSTEM.ADDRESS;     -- расширение

           END;


TYPE

  INI   = PROCEDURE (VAR io_ptr,        -- дескриптор в/в

                     ARRAY OF CHAR,     -- имя модуля/файла

                     INTEGER,           -- вид модуля/файла

                     PRINT              -- для сообщений

                     );

  EXI   = PROCEDURE (VAR io_ptr);

  ERROR = PROCEDURE (

                     INTEGER,           -- номер строки

                     INTEGER,           -- позиция в строке

                     ARRAY OF CHAR,     -- строка текста

                     ARRAY OF CHAR,     -- формат

                     SEQ SYSTEM.WORD    -- аргументы

                     );

END coolDefs.

Модуль определяет тип дескриптора ввода/вывода, виды модулей/файлов, вид операций открытия/закрытия дескриптора и сообщения об ошибках.

В процедуре сообщения об ошибках первые два параметра определяют позицию ошибки в тексте, а последние два – текст поясняющего сообщения.

Модуль coolIO реализует операции файлового ввода/вывода.

DEFINITION MODULE coolIO; (* Ned 04-Mar-90. (c) KRONOS *)


IMPORT comp: coolDefs;


PROCEDURE ini(VAR io: comp.io_ptr;

                name: ARRAY OF CHAR;

                unit: INTEGER;

               print: comp.PRINT);


PROCEDURE exi(VAR io: comp.io_ptr);


PROCEDURE set(text_path,sym_path,out: ARRAY OF CHAR; print: comp.PRINT);


PROCEDURE dispose;


END coolIO.

Модуль mxPars реализует операцию compile, которая

параметризована операциями ввода/вывода.

DEFINITION MODULE mxPars; (* Ned 06-Dec-87. (c) KRONOS *)


IMPORT SYSTEM;


IMPORT comp: defCompiler;


PROCEDURE compile(text : comp.io_ptr;

                   ini : comp.INI;

                   exi : comp.EXI;

                  error: comp.ERROR;

                  print: comp.PRINT;

                  opts : BITSET;

                   cpu : INTEGER;

                   );


PROCEDURE fault(format: ARRAY OF CHAR; SEQ args: SYSTEM.WORD);


END mxPars.

Процедура compile в качестве параметров получает открытый дескриптор файла входного текста, процедуры открытия/закрытия дескриптора, процедуры вывода ошибок и сообщений, множество режимов компилятора и номер версии системы команд.



Глава 5.4. Средства отладки и визуализации

К средствам отладки программ относятся историк, выдающий историю неудавшегося процесса в терминах текста программы, и визуализатор кода и других атрибутов программы.

5.4.1. Визуализация М-кода

Визуализацию кода осуществляет утилита vx.

Для работы утилиты необходим, как минимум, кодофайл. При этом можно узнать следующее:

  • коды процедур (имена процедур недоступны);
  • строковый пул;
  • список внешних и, при необходимости, прогуляться по дереву импорта;
  • мультиглобалы;
  • общую информацию о модуле (версия компилятора, время компиляции и т.д.).

Кроме того, при наличии реффайла утилита выдает информацию "по полной программе":

  • описание процедуры: имя, параметры, локалы;
  • типы;
  • глобалы: имя, тип;
  • структурные константы.

Подробное описание утилиты vx читайте в справочнике по утилитам.

5.4.2. Посмертный историк

Историю неудавшегося процесса выдает утилита hi.

Подробное описание утилиты hi читайте в справочнике по утилитам.

5.4.3. Симфайлы, кодофайлы и реффайлы

Этот пункт пока не написан. Возможно, он появится не скоро, или даже вовсе никогда не появится, потому что информация, которую ему следовало бы содержать, по мнению разработчика, является делом текущей версии компилятора и подвержена периодическим изменениям. Как следствие, эта информация уже изложена в соответствующих "холодных" (cool) библиотеках.

Читайте о cимфайлах в комментарии к библиотеке coolSym.

Читайте о кодофайлах в комментарии к библиотеке visCode.

Читайте о реффайлах в комментарии к библиотеке xRef.



Глава 5.5. Еще о Модула-X компиляторе

Предисловие соавтора

Этот раздел написан А.Недорей (Ned), автором ныне здравствующего Модула-компилятора (mx) вскоре после создания последнего. Первоначально это задумывалось как статья, не предназначающаяся для публикации в официальных изданиях. Поэтому я рада, что изложенная в ней информация впервые увидит свет на страницах нашей книги. Этот материал адресуется разработчикам компиляторов, отладчиков и комплексаторов (?), а также посмертным историкам.



ВВЕДЕНИЕ
В котором автор попробует понять,
зачем ему понадобилось писать еще один
Модула-2 компилятор

Причины (как обычно) бывают субъективные и объективные, причем важными являются только субъективные (в данном случае - ровно одна субъективная причина), а объективные предназначены для того, чтобы убедить себя и других в необходимости делать то, что хочется. Так вот, субъективная причина была очень проста: хотелось написать компилятор, который можно развивать, который послужит основой для других разработок.

Теперь перечислим те объективные соображения, которые послужили толчком к началу разработки компилятора. Они приводятся в некотором случайном порядке (не по важности).

1. Любое программное обеспечение морально устаревает, принципы его построения, интерфейсы с ОС, СП и пользователем с какого-то момента не соответствует изменившимся взглядам разработчиков и пользователей.

2. Если два года назад (когда был написан наш предыдущий компилятор, далее - m2) проблемы переносимости нас не волновали (хоть бы для Кроносов что-нибудь написать), то теперь мы собираемся переносить свое ПО на все 32-разрядные микропроцессоры, и в том числе на разные Кроносы (а система команд для кристалла (4.X) достаточно существенно отличается от системы команд семейства 2.X).

3. При разработке и эксплуатации компилятора m2 входной язык был расширен, часть этих изменений стали стандартными, часть в процессе использования были признаны ненужными. Разработка нового компилятора позволяет реализовать принятые расширения естественно (без ограничений), так как набор расширений известен с начала разработки.

4. И так далее.

КОНЕЦ Введения.


ПРИМЕЧАНИЕ:

Далее в тексте используется терминология, введенная в [5,6], например, иннерфейс, экстерфейс [5], фильтр [6].


ГЛАВА ПЕРВАЯ
В которой изложены особенности входного языка

Особенности входного языка изложены в документации (см).


ГЛАВА ВТОРАЯ
(Из трех взглядов).
В которой автор показывает mx со всех трех сторон.

ВЗГЛЯД 1. mx со стороны пользователя

Вид с этой стороны не должен вызвать интереса у людей, знакомых с m2. Как и m2, mx един в нескольких лицах:

  • пакетная версия (утилита mx);
  • турбо-компилятор (фильтр turbo2x);
  • турбо-тестер (фильтр tester2x);
  • и так далее.

Пакетная версия читает исходный текст из файла и пишет результаты компиляции (кодофайл и симфайл) на диск.

Фильтр turbo2x читает текст из буфера редактора, а результаты пишет в файлы.

Турбо-тестер (см. главу 3) используется для отладки компилятора. Он читает текст (множество единиц компиляции) из буфера редактора, пытается выполнить те тесты, которые скомпилировались, сравнивает результаты компиляции и запуска с тем, что должно было быть, и сообщает о расхождениях. Кодофайлы и симфайлы хранятся в оперативной памяти и уничтожаются после выполнения теста.


ВЗГЛЯД 2. mx со стороны разработчика системы программирования

Все утилиты, описанные в первом взгляде, пользуются некоторой библиотекой, которая позволяет просто писать разные (с точки зрения пользователя) версии Модула-компиляторов. Собственно говоря, эта библиотека и есть mx, а все утилиты реализуют методы ее использования. Внутри данного взгляда под словом "пользователь" мы будем понимать пользователя этой "библиотеки" (т.е. разработчика очередного "пускача").

Библиотека mx - это слоеный пирог (терминология из [5]):

+---------------------------+
|   Интерфейс с системой    |
|        (иннерфейс)        |
+---------------------------+
+---------------------------+
|        ЧЕРНЫЙ ЯШИК        |
+---------------------------+
+---------------------------+
| Интерфейс с пользователем |
|       (экстерфейс)        |
+---------------------------+

Интерфейс с системой определяет модуль mxSystem. Этот модуль предоставляет остальному компилятору все возможности, которые нужны от системы, кроме операций чтения исходного текста, записи выходных файлов и выдачи сообщений об ошибках. Операции ввода/вывода определяются каждой компилирующей утилитой.

Интерфейс с пользователем определяют модули defCompiler, mxPars и mxIO. Модуль defCompiler определяет представление операций ввода/вывода. Модуль mxPars реализует операцию compile, которая параметризована операциями ввода/вывода. Модуль mxIO предоставляет стандартные реализации операций ввода/вывода.

Продемонстрируем использование этих модулей при написании пакетного компилятора:

MODULE компилятор;


IMPORT comp: defCompiler;

IMPORT mx: mxPars;

IMPORT io: mxIO;


VAR

  text: io.io_ptr;

  name: ARRAY [0..255] OF CHAR;


BEGIN

  инициализация;

  WHILE есть_что_компилировать DO

    следующее_имя_файла(name); (*0*)

    io.ini(text,name,comp.text,mx.fault); (*1*)

    IF text^.done THEN (*2*)

      mx.compile(text,io.ini,io.exi,....); (*3*)

    END;

    io.exi(text); (*4*)

  END;

END компилятор.

(*0*) Выбор следующего имени файла;
(*1*) Начало работы с текстом из файла с именем name;
(*2*) Если файл успешно открылся;
(*3*) Компиляция;
(*4*) Конец работы с текстом.


ВЗГЛЯД 3. mx изнутри

Теперь рассмотрим то, что находится в черном ящике для взгляда 2. Компилятор состоит из 5 модулей:

  • mxScan -- лексический анализ;
  • mxObj  -- таблица объектов, видимость, импорт/экспорт;
  • mxSym  -- чтение/запись симфайлов;
  • mxCmd  -- примитивы генерация кода;
  • mxGen  -- генерация кода;
  • mxPars -- синтаксический анализ.
----------------------------------------------------
   Модуль  |        Число строк       | размер кода
           | определение | реализация | (в байтах)
-----------+-------------+------------+-------------
 mxScan    |     115     |     646    |     3624
 mxObj     |     279     |     679    |     2988
 mxSym     |      13     |     602    |     3468
 mxCmd     |     215     |    1780    |     7184
 mxGen     |     254     |    2177    |    11084
 mxPars    |      39     |    1486    |     7732
-----------+-------------+------------+-------------
 ИТОГО     |     915     |    7370    |    36080
----------------------------------------------------

Все генерация сосредоточена в модулях mxGen и mxCmd. Модуль mxCmd содержит примитивы генерации (аппаратно-зависимые), а модуль mxGen языково-зависимую генерацию. При генерации делается щелевая оптимизация (аналогично m2) и оптимизация структур управления. На тестах со сложными управляющими структурами оптимизация управления может дать существенное ускорение. Так, известный тест "dhrystone", скомпилированный mx, работает на 10% быстрее по сравнению с m2.

Интерфейс с модулем mxGen выдержан в машинно-независимых терминах, что позволяет надеяться на простоту переноса компилятора на другие архитектуры.


ГЛАВА ТРЕТЬЯ
В которой описывается процесс отладки компилятора

Для отладки компилятора использовался турбо-тестер (упомянутый в предыдущей главе) и набор тестов [7], дополненный автором. Набор тестов содержит тесты, проверяющие корректность реализации различных конструкций языка, тесты на граничные условия, тесты на исключительные ситуации и диагностику ошибок.

Примеры:

Следующие два теста проверяют корректность реализации следующего правила из описания языка:

"Тип Т1 может использоваться в описании типа указателя Т, тесктуально предшествующего описанию Т1, если Т и Т1 описаны в одном блоке."

Первый тест проверяет то, что такое описание типа указателей разрешено; второй - то, что компилятор проверяет ограничение правила, то есть что оба типа должны быть описаны в одном блоке.

$x

MODULE T6P7D4; (* Conformance *)


FROM InOut IMPORT WriteString;


TYPE T = POINTER TO T1;


TYPE T1 = RECORD

            X: INTEGER;

            Y: BOOLEAN

          END;


BEGIN

  WriteString('PASS...6.7-4',12)

END T6P7D4.


$

MODULE T6P7D5; (* Deviance *)


FROM InOut IMPORT WriteString;


TYPE P = POINTER TO T;


PROCEDURE p;

  TYPE T = BOOLEAN;

BEGIN

  WriteString('DEVIATES...6.7-5',16)

END p;


BEGIN

  p;

END T6P7D5.

Специальные строки в тексте, начинающиеся символом '$', указывают турбо-тестеру, что должна дать проверка данным тестом.

УправляющаяСтрока = "$" [ "c" | "x" ]

Символ "x" указывает, что тест должен быть скомпилирован и удачно исполнен; символ "c" - что тест должен быть скомпилирован и неудачно исполнен (исключительная ситуация при исполнении); отсутствие символов указывает на то, что компилятор должен обнаружить ошибки в тесте. Турбо-тестер выдает список имен тестов, при проверке которых результат не соответстввует требуемому.

Следующий тест проверяет, ведется ли контроль границ индексов при исполнении программы.

$c

MODULE T8P1D5;


FROM InOut IMPORT WriteString;


VAR a: ARRAY [2..5] OF INTEGER;

    i: INTEGER;


BEGIN

  i:=1;

  A[i]:=1;

  WriteString('ERROR IS NOT DETECTED...8.1-5',30)

END T8P1D5.

Турбо-тестер позволяет исполнить полный (около 250 штук) или частичный набор тестов после каждого изменения компилятора и проверить корректность этого изменения и то, что изменение не повлияло на реализацию других конструкций языка. Благодаря наличию системы тестов и мощной поддержки тестирования удалось очень быстро получить надежный и стабильный компилятор.


ГЛАВА ЧЕТВЕРТАЯ
В которой автор говорит всем,
кто поддерживал его в этой работе
и отдельно Д.Г.Фон-Дер-Флаассу,
принимавшему активное участие в разработке алгоритмов
оптимизации структур управления,
БОЛЬШОЕ СПАСИБО



Л И Т Е Р А Т У Р А

  1. N.Wirth Programming in Modula-2. Third, Corrected Edition. Springer-Verlag. Berlin, Heidelberg, New-York, 1985.
  2. N.Wirth. A fast and compact compiler for Modula-2. ETH, Zurich, July 1985, 64, pp. 1-22.
  3. N.Wirth. The Programming Language Oberon.
  4. Second Working Draft Modula-2 Standard. Document D103. BSI Modula-2 Standartisation Working Group (IST/5/13).
  5. Н.Вирт Программирование на языке Модула-2. Москва, Мир, 1987.
  6. Кузнецов Д.Н., Недоря А.Е., Тарасов Е.В., Филиппов В.Э. КРОНОС - автоматизированное рабочее место профессионального программиста. В сб.: Автоматизированное рабочее место программиста. Новосибирск, 1988.
  7. Кузнецов Д.Н., Недоря А.Е. Турбо-компиляция. Что дальше? (рукопись).


Содержание Части 1-5 Части 7-8 Части 9-11

Скачать PDF