Программирование на Обероне начинается с выбора компилятора. Многие из нас уже определились с основным компилятором (средой) и пользуются только им, или ещё и посматривают в сторону другого, особо его не “обживая”. Некоторые предпочли AOS или BlackBox. Имеется также превосходный безплатный оптимизирующий компилятор XDS, который, вероятно, является наилучшей реализацией языка Оберон-2. Однако политика Excelsior в отношении этого компилятора остаётся неясной. Омрачает прогнозы тот факт, что
Александр Ильин обнаружил и забагрепортил несколько найденных им в компиляторе ошибок, которые исправлять разработчики совсем не торопятся. Как и не собираются открывать исходники, чтобы Оберон-сообщество получило возможность исправить ошибки своими силами или доработать компилятор с учётом требований, возникших за последнее десятилетие (например, сделать INTEGER 32-битным или ввести поддержку юникод-строк).
Всё это заставляет искать другие компиляторы. И недавним открытием (благодаря
Александру Ильину) стал для меня консольный компилятор ETH Oberon для Win32, которым, ввиду его малой распространённости, хочу поделиться и с вами. О плюсах:
1. Начнём с того, что компилятор этот — вовсе не студенческая поделка. Это канонический Оберон из альма-матер ETH. Вот здесь (
http://dic.academic.ru/dic.nsf/ruwiki/1070131) есть такое высказывание:
“Наиболее зрелыми, видимо, следует считать ETH Oberon, реализации которого имеются для многих платформ, и Компонентный Паскаль. Есть несколько систем разработки программ для различных вариантов языка Оберон: ETH Oberon, доступная для многих вычислительных платформ”. Да, это тот самый компилятор, на котором разработана ETH Oberon System 3.
2. Для нас ценно то, что с ним можно работать не внутри Оберон-системы, а прямо из-под Windows. Качество исполняемого кода мною не изучалось, но уверен, что оно не хуже, чем у компилятора BlackBox.
3. Есть наработки кода, сделанные именно на этом компиляторе. Я подробно не искал, но, вероятно, с минимальными правками соберётся много чего даже из исходников AOS. Также я сделал ограниченную поддержку данного компилятора и в своём проекте “SDL for Oberon” (
http://sourceforge.net/projects/sdl-for-oberon).
4. ETH Oberon
частично поддерживает Active Oberon. Подчёркиваю,
не весь Активный Оберон, только его подмножество. AO тоже родился в ETH, и компилятор ETH закономерно поддерживает его какую-то часть. Например, переопределение операций:
Код:
(** Comparison Operators*)
PROCEDURE "<="* (a, b: HUGEINT): BOOLEAN;
BEGIN RETURN Compare()#Gt
END "<=";
PROCEDURE "="* (a, b: HUGEINT): BOOLEAN;
BEGIN RETURN Compare()=Eq
END "=";
PROCEDURE "#"* (a, b: HUGEINT): BOOLEAN;
BEGIN RETURN Compare()#Eq
END "#";
Запросто собирается следующее:
Код:
MODULE ReaderWriter;
TYPE
RW = OBJECT
(* n = 0, пусто *)
(* n < 0, n писателей *)
(* n > 0, n читателей *)
VAR n: LONGINT;
PROCEDURE EnterReader*;
BEGIN {EXCLUSIVE}
AWAIT(n >= 0); INC(n)
END EnterReader;
PROCEDURE ExitReader*;
BEGIN {EXCLUSIVE}
DEC(n)
END ExitReader;
PROCEDURE EnterWriter*;
BEGIN {EXCLUSIVE}
AWAIT(n = 0); DEC(n)
END EnterWriter;
PROCEDURE ExitWriter*;
BEGIN {EXCLUSIVE}
INC(n)
END ExitWriter;
PROCEDURE & Init;
BEGIN n := 0
END Init;
END RW;
END ReaderWriter.
5. Также поддержан встроенный ассемблер, что вообще редкость для компиляторов Оберона. Между тем, что может быть нагляднее для восприятия кодовых вставок в программе, чем ассемблер?
Код:
PROCEDURE LIntToHInt*(i: LONGINT): HUGEINT;
CODE {SYSTEM.i386}
MOV EAX, i[EBP]
CDQ
MOV EBX, 12[EBP]
MOV 0[EBX], EAX
MOV 4[EBX], EDX
END LIntToHInt;
PROCEDURE RealToHInt*(r: REAL): HUGEINT;
CODE {SYSTEM.i386, SYSTEM.FPU}
FLD DWORD r[EBP]
MOV EAX, 12[EBP]
FISTP QWORD [EAX]
WAIT
END RealToHInt;
PROCEDURE LRealToHInt*(r: LONGREAL): HUGEINT;
CODE {SYSTEM.i386, SYSTEM.FPU}
FLD QWORD r[EBP]
MOV EAX, 16[EBP]
FISTP QWORD [EAX]
WAIT
END LRealToHInt;
6. Ну и, наконец, в отличие от BlackBox и XDS — компилятор ETH полностью свободный и с открытыми исходниками. А поскольку он изначально спроектирован как портабельный, и есть реализации для других систем, то, вероятно, нетрудно будет собрать на основе приложенных исходников консольный компилятор ETH Oberon, генерирующий код для других платформ, отличных от 80x86, и линкер, создающий исполняемые файлы для других платформ. Безусловно, наиболее интересует создание исполняемых файлов и библиотек в формате ELF для Linux.
Минусы.
1. Слабая “обвязка” вокруг Windows, базовые привязки к dll придётся разрабатывать самостоятельно.
2. Неудобный линкер, требующий вручную прописывать импортируемые процедуры из подлинковываемых dll-библиотек.
3. Неудобный вывод позиции ошибки. Выводится не строка и позиция, а абсолютное число, высчитывать приходится вручную. Я не делал попыток прикрутить компилятор к текстовому редактору, чтобы курсор автоматически устанавливался в позицию ошибки, но буду рад увидеть подобные разработки, например, с Obide. (Другой вариант — рулит SynEdit со схемой подсветки синтаксиса Modula-3. Для Оберона пользуюсь, удобно).
4. Не удалось объявить на уровне типа нетрассируемый указатель. На атрибуты [UNTRACED], [NOTAG] и [C] при объявлении типа компилятор неизменно ругается “err 200 not yet implemented”. Это довольно серьёзный недостаток, потому что теряется возможность полноценно возвращать нетрассируемый указатель, а объявлять его, получается, можно лишь на уровне переменных. Т.е. не работает:
Код:
TYPE
PRect* = POINTER [UNTRACED] TO Rect;
Также не работает:
Код:
TYPE
PRect* [UNTRACED] = POINTER TO Rect;
Работает лишь так:
Код:
TYPE
PRect* = POINTER TO Rect;
VAR
rect* [UNTRACED]: PRect;
Такая реализация лишает нас возможности экспортировать на уровне типа нетрассируемый указатель, а также вернуть из процедуры нетрассируемый указатель на запись. Поэтому пришлось сделать вместо:
Код:
TYPE
PSurface* = POINTER TO Surface;
Surface* = RECORD [NOTAG]
…
END;
VAR
LoadBMPRW-: PROCEDURE [C] (src: PRWops; freesrc: Integer): PSurface;
так:
Код:
TYPE
Pointer = LONGINT;
VAR
LoadBMPRW-: PROCEDURE [C] (src: PRWops; freesrc: Integer): Pointer (*PSurface*);
Если есть решение, которое я упустил, пожалуйста, дайте знать.
Кстати, решена ли эта проблема в Active Oberon? Насколько я понимаю, тоже нет (
http://www.ocp.inf.ethz.ch/forum/index.php/topic,450.0.html). Невозможность экспортировать тип нетрассируемый указатель или безхитростно возвратить из процедуры (особенно написанной не на Обероне) нетрассируемый указатель на запись лично мне, после работы на BlackBox и XDS, кажется корявостью. Это позволяет предположить, что AO и ETH Oberon затрудняют интеграцию с другими хорошо зарекомендовавшими себя решениями, что они более “вещи в себе”, чем XDS и BlackBox, ибо последние более дружественны к интеграции с наработками на других языках/ОС.
5. Символ "_" в идентификаторах не поддержан наглухо. С чем это связано — остаётся только догадываться. Здесь я вспомнил также компилятор Oberon-M, который попортил мне этим немало крови, но заставил понемногу отказаться от символа “_” вообще. Полагаю, у мэтра Вирта есть причина невзлюбить этот символ в идентификаторах.
Как видите, компилятор совсем неплох, и я решился пропиарить его, поскольку мало кто из опрошенных мною оберонщиков о нём слышал; мне понравились и встроенный ассемблер, и поддержка подмножества Active Oberon, более того, я начал адаптацию библиотеки SDL именно для этого компилятора, а не для BlackBox. Этим и объясняются некоторые решения, в частности, отказ от символа "_" в идентификаторах. Это может показаться необоснованным, но важно для создания той самой единой языковой Оберон-базы, которая позволит свободно перейти с одного компилятора на другой, не перелопачивая при этом много кода (В идеале — вообще не изменяя).
Ну и, наконец, мечты. Хотелось бы иметь порт данного консольного компилятора для Linux, а также совсем уж хорошо иметь консольный компилятор для Win32/Linux, добытый из AOS. Надеюсь, кто-то займётся. Также хочу особенно обозначить важность кросс-компиляторных средств, ориентированных на Оберон как на языковую платформу, чтобы можно было в процессе разработки изменить компилятор. (Например, если возникла необходимость перейти на безплатный компилятор). Это подразумевает как общие по интерфейсным вызовам и логике модули привязки к сторонним библиотекам, так и оригинальные Оберон-разработки, которые будут вестись целенаправленно для нескольких компиляторов одновременно. Напомню, пресловутая кроссплатформенность языка Си зиждется именно на одинаковом по интерфейсу наборе библиотек типа stdio.h, strings.h и т.д. для каждой из платформ, что дало повод заявить о платформенной независимости части программ (особенно консольных), разработанных на Си. Пока что я не видел таких кросс-компиляторных наработок для языков Оберон-семейства. А отход от заглавных символов в идентификаторах в Zonnon меня, честно говоря, даже расстроил. По привычке это удобно даже при работе в Delphi.
P.S. Результаты работ по адаптации SDL к ETH Oberon выложены на
http://sourceforge.net/projects/sdl-for-oberon, плюс маленькая демка. Сделано немного, делюсь тем, что сделал, как некий своеобразный результат небольшого исследования данного компилятора. Большего пока не планируется. Полная поддержка SDL для ETH Oberon возможна при решении проблемы объявления на уровне типов нетрассируемого указателя. Впрочем, для SDL я вижу ещё одно решение: создать объектную обвязку. Однако этим мне заниматься не хочется, да и времени нет.
P.P.S. Вопрос к знающим людям: возможно ли на основе приложенных исходников собрать консольные компилятор и линкер Active Oberon, порождающие независимые от AOS EXE-файлы для Windows?