Система: текст вместо кнопок
Самое непривычное в Обероне — не язык и не процессор, а интерфейс. Здесь нет кнопок, меню, диалогов и значков. Есть текст, и любой текст можно исполнить.
Эту главу стоит прочесть до первой лабораторной, иначе система выглядит сломанной.
Средняя кнопка исполняет
Основное правило: средний щелчок по слову вида Модуль.Процедура запускает
эту процедуру. Не по кнопке, не по пункту меню — по любому такому слову, где
бы оно ни было написано: в списке команд, в письме, в комментарии, в
собственном тексте, который вы только что набрали.
На мыши без средней кнопки её заменяет Alt с левой.
Отсюда следует то, что поначалу сбивает: в системе нет разницы между
«документом» и «панелью инструментов». Правая нижняя область с текстом
System.Open, ORP.Compile, Tools.Inspect — это обычный текстовый файл
System.Tool. Его можно редактировать, дописывать в него свои команды и
сохранять. То, что вы сами набрали, работает ровно так же, как то, что шло
в комплекте.
Три кнопки
Каждая кнопка мыши делает своё:
| кнопка | действие |
|---|---|
| левая | поставить курсор (место вставки) |
| средняя | исполнить команду под указателем |
| правая | выделить текст протяжкой |
Выделение и курсор — разные вещи и существуют одновременно. Это важно: многие команды берут аргумент из выделения, а вставляют результат туда, где стоит курсор.
Стрелка и тильда
В списке команд встречаются два знака-подсказки.
Стрелка ↑ после имени команды означает: аргумент берётся из текущего
выделения. System.Directory ↑ — выделите образец вроде *.Mod, затем
щёлкните по команде.
Тильда ~ закрывает список параметров. ORP.Compile Math.Mod/s ~ — команда,
имя файла, ключ и тильда. Без тильды команда прочитает лишнее и, скорее всего,
пожалуется.
Окна
Экран разделён на вертикальные полосы, в каждой — стопка окон. У каждого окна своя чёрная полоска заголовка, и в ней — команды, относящиеся к этому окну:
System.Log | Edit.Locate Edit.Search System.Copy System.Grow System.Clear
System.Close закрывает, System.Grow растягивает, Edit.Store сохраняет.
Это тот же механизм: слова в полоске — обычный текст, исполняемый средним
щелчком.
Два окна открыты сразу после загрузки. Сверху — System.Log, куда пишут все сообщения: результаты компиляции, ошибки, отчёты команд. Снизу — System.Tool, список команд.
У журнала есть особенность, на которую стоит обратить внимание заранее: он
показывает текст с начала и сам не прокручивается. Если вывод длиннее
примерно восемнадцати строк, хвост просто не виден, хотя команда отработала.
System.Clear в его заголовке очищает журнал — пользуйтесь им между
экспериментами.
Обычный рабочий путь
Скомпилировать модуль:
- левым щелчком поставить курсор в конец текста
System.Tool; - набрать
ORP.Compile Имя.Mod/s ~; - средним щелчком по набранному запустить.
В журнале появится строка вида:
compiling Math 447 0 32C32F12
Это имя модуля, размер кода в словах, размер данных в байтах и ключ интерфейса. Про ключ — в главе про модули.
Ключ /s разрешает перезаписать символьный файл. Без него компилятор
откажется менять интерфейс модуля, от которого кто-то зависит.
Открыть файл на правку: Edit.Open Имя.Mod, сохранить: Edit.Store в
заголовке окна.
Одна вещь, которая удивляет
Система однозадачная в том смысле, что команда выполняется до конца, и всё это время интерфейс не отвечает. Но между командами работает сборщик мусора — и только между ними.
Практическое следствие вы встретите сразу: если попросить компилятор собрать несколько крупных модулей одной командой, таблицы символов накопятся и куча кончится. Появится
pos 6734 TRAP 4 in ORB at 0001EC10
Это не поломка компилятора и не ошибка в вашем коде: ловушка номер 4 —
разыменование NIL, а NIL вернуло выделение памяти. Те же модули, собранные
по одному за команду, проходят без единой жалобы.
Мы наступили на это дважды: сперва при сборке компилятора, потом при пересборке всей системы. Правило простое — один крупный модуль на команду.
Задачи вместо потоков
Почему именно между командами? Потому что в системе нет ни потоков, ни
прерываний таймера — есть один цикл, Oberon.Loop. Он читает мышь и
клавиатуру, а когда ввода нет, по кругу вызывает задачи: процедуры,
поставленные в круг вызовом Oberon.Install. Задача обязана быстро вернуть
управление — отобрать его у неё нечем. Пока работает команда или задача, не
происходит больше ничего: не движется указатель мыши, не мигает курсор, не
работают другие задачи. Бесконечный цикл в задаче убивает систему целиком.
Всё это вы проделаете сами в лабораторной №11.
Сборщик мусора — тоже задача. Последние строки Oberon.Mod:
ActCnt := 0; CurTask := NewTask(GC, 1000); Install(CurTask);
Раз в секунду цикл вызывает GC, но убирает он только по одному из двух
поводов: с прошлой уборки прошло BasicCycle = 20 действий человека (нажатий
и щелчков) или до конца кучи осталось меньше 64 КБ. System.Collect ничего не
убирает сам — только обнуляет счётчик действий.
И главное: отмечает сборщик от глобальных указателей модулей
(Kernel.Mark(mod.ptr)), стек он не просматривает вовсе. Посреди команды
живые объекты держатся её локальными переменными, и уборка выбросила бы их.
Поэтому — только между командами, когда стек пуст. Кучу до и после уборки,
по переменной Kernel.allocated, вы увидите в лабораторной №10.