Компилятор языка программирования Oberon-07/16 для x86_64 Windows/Linux ------------------------------------------------------------------------------ Параметры командной строки Вход - текстовые файлы модулей с расширением ".ob07", кодировка ANSI или UTF-8 с BOM-сигнатурой. Выход - испоняемый файл формата PE32+ или ELF64. Параметры: 1) имя главного модуля 2) тип приложения "win64con" - Windows64 console "win64gui" - Windows64 GUI "win64dll" - Windows64 DLL "linux64exe" - Linux ELF64-EXEC "linux64so" - Linux ELF64-SO 3) необязательные параметры-ключи -out имя результирующего файла; по умолчанию, совпадает с именем главного модуля, но с другим расширением (соответствует типу исполняемого файла) -stk размер стэка в мегабайтах (по умолчанию 2 Мб, допустимо от 1 до 32 Мб) -tab размер табуляции (используется для вычисления координат в исходном коде), по умолчанию - 4 -nochk <"ptibcwra"> отключить проверки при выполнении -lower разрешить ключевые слова и встроенные идентификаторы в нижнем регистре -def <имя> задать символ условной компиляции -uses вывести список импортированных модулей параметр -nochk задается в виде строки из символов: "p" - указатели "t" - типы "i" - индексы "b" - неявное приведение INTEGER к BYTE "c" - диапазон аргумента функции CHR "w" - диапазон аргумента функции WCHR "r" - эквивалентно "bcw" "a" - все проверки Порядок символов может быть любым. Наличие в строке того или иного символа отключает соответствующую проверку. Например: -nochk it - отключить проверку индексов и охрану типа. -nochk a - отключить все отключаемые проверки. Например: Compiler.exe "C:\example.ob07" win64con -out "C:\example.exe" -stk 1 Compiler.exe "C:\example.ob07" win64dll -out "C:\example.dll" -nochk pti Compiler "source/Compiler.ob07" linux64exe -out "source/Compiler" -nochk a В случае успешной компиляции, компилятор передает код завершения 0, иначе 1. ------------------------------------------------------------------------------ Отличия от оригинала 1. Расширен псевдомодуль SYSTEM 2. В идентификаторах допускается символ "_" 3. Добавлены системные флаги 4. Усовершенствован оператор CASE (добавлены константные выражения в метках вариантов и необязательная ветка ELSE) 5. Расширен набор стандартных процедур 6. Семантика охраны/проверки типа уточнена для нулевого указателя 7. Добавлены однострочные комментарии (начинаются с пары символов "//") 8. Разрешено наследование от типа-указателя 9. Добавлен синтаксис для импорта процедур из внешних библиотек 10. "Строки" можно заключать также в одиночные кавычки: 'строка' 11. Добавлен тип WCHAR 12. Добавлена операция конкатенации строковых и символьных констант 13. Возможен импорт модулей с указанием пути и имени файла 14. Добавлен специальный синтаксис для условной компиляции (см. CC.txt) 15. Имя процедуры в конце объявления (после END) необязательно ------------------------------------------------------------------------------ Особенности реализации 1. Основные типы Тип Диапазон значений Размер, байт INTEGER -9223372036854775808 .. 9223372036854775807 8 REAL 4.94E-324 .. 1.70E+308 8 CHAR символ ASCII (0X .. 0FFX) 1 BOOLEAN FALSE, TRUE 1 SET множество из целых чисел {0 .. 63} 8 BYTE 0 .. 255 1 WCHAR символ юникода (0X .. 0FFFFX) 2 2. Максимальная длина идентификаторов - 255 символов 3. Максимальная длина строковых констант - 511 символов (UTF-8) 4. Максимальная размерность открытых массивов - 5 5. Процедура NEW заполняет нулями выделенный блок памяти 6. Глобальные и локальные переменные инициализируются нулями 7. В отличие от многих Oberon-реализаций, сборщик мусора и динамическая модульность отсутствуют 8. Тип BYTE в выражениях всегда приводится к INTEGER 9. Контроль переполнения значений выражений не производится 10. Ошибки времени выполнения: 1 ASSERT(x), при x = FALSE 2 разыменование нулевого указателя 3 целочисленное деление на неположительное число 4 вызов процедуры через процедурную переменную с нулевым значением 5 ошибка охраны типа 6 нарушение границ массива 7 непредусмотренное значение выражения в операторе CASE 8 ошибка копирования массивов v := x, если LEN(v) < LEN(x) 9 CHR(x), если (x < 0) OR (x > 255) 10 WCHR(x), если (x < 0) OR (x > 65535) 11 неявное приведение x:INTEGER к v:BYTE, если (x < 0) OR (x > 255) ------------------------------------------------------------------------------ Псевдомодуль SYSTEM Псевдомодуль SYSTEM содержит низкоуровневые и небезопасные процедуры, ошибки при использовании процедур псевдомодуля SYSTEM могут привести к повреждению данных времени выполнения и аварийному завершению программы. PROCEDURE ADR(v: любой тип): INTEGER v - переменная или процедура; возвращает адрес v PROCEDURE SADR(x: строковая константа (CHAR UTF-8)): INTEGER возвращает адрес x PROCEDURE WSADR(x: строковая константа (WCHAR)): INTEGER возвращает адрес x PROCEDURE SIZE(T): INTEGER возвращает размер типа T PROCEDURE TYPEID(T): INTEGER T - тип-запись или тип-указатель, возвращает номер типа в таблице типов-записей PROCEDURE INF(): REAL возвращает специальное вещественное значение "бесконечность" PROCEDURE MOVE(Source, Dest, n: INTEGER) Копирует n байт памяти из Source в Dest, области Source и Dest не могут перекрываться PROCEDURE GET(a: INTEGER; VAR v: любой основной тип, PROCEDURE, POINTER) v := Память[a] PROCEDURE GET8(a: INTEGER; VAR x: INTEGER, SET, BYTE, CHAR, WCHAR, SYSTEM.CARD32) Эквивалентно SYSTEM.MOVE(a, SYSTEM.ADR(x), 1) PROCEDURE GET16(a: INTEGER; VAR x: INTEGER, SET, WCHAR, SYSTEM.CARD32) Эквивалентно SYSTEM.MOVE(a, SYSTEM.ADR(x), 2) PROCEDURE GET32(a: INTEGER; VAR x: INTEGER, SET, SYSTEM.CARD32) Эквивалентно SYSTEM.MOVE(a, SYSTEM.ADR(x), 4) PROCEDURE PUT(a: INTEGER; x: любой основной тип, PROCEDURE, POINTER) Память[a] := x; Если x: BYTE или x: WCHAR, то значение x будет расширено до 64 бит, для записи байтов использовать SYSTEM.PUT8, для WCHAR -- SYSTEM.PUT16 PROCEDURE PUT8(a: INTEGER; x: INTEGER, SET, BYTE, CHAR, WCHAR, SYSTEM.CARD32) Память[a] := младшие 8 бит (x) PROCEDURE PUT16(a: INTEGER; x: INTEGER, SET, BYTE, CHAR, WCHAR, SYSTEM.CARD32) Память[a] := младшие 16 бит (x) PROCEDURE PUT32(a: INTEGER; x: INTEGER, SET, BYTE, CHAR, WCHAR, SYSTEM.CARD32) Память[a] := младшие 32 бит (x) PROCEDURE COPY(VAR Source: любой тип; VAR Dest: любой тип; n: INTEGER) Копирует n байт памяти из Source в Dest. Эквивалентно SYSTEM.MOVE(SYSTEM.ADR(Source), SYSTEM.ADR(Dest), n) PROCEDURE CODE(byte1, byte2,... : BYTE) Вставка машинного кода, byte1, byte2 ... - константы в диапазоне 0..255, например: SYSTEM.CODE(048H,08BH,045H,010H) (* mov rax,qword[rbp+16] *) Также, в модуле SYSTEM определен тип CARD32 (4 байта). Для типа CARD32 не допускаются никакие явные операции, за исключением присваивания. Функции псевдомодуля SYSTEM нельзя использовать в константных выражениях. ------------------------------------------------------------------------------ Системные флаги При объявлении процедурных типов и глобальных процедур, после ключевого слова PROCEDURE может быть указан флаг соглашения о вызове: [win64], [systemv], [windows], [linux], [oberon], [ccall]. Например: PROCEDURE [win64] MyProc (x, y, z: INTEGER): INTEGER; Флаг [windows] - синоним для [win64], [linux] - синоним для [systemv]. Флаг [ccall] - синоним для [win64] или [systemv] (зависит от целевой ОС). Знак "-" после имени флага ([win64-], [linux-], ...) означает, что результат процедуры можно игнорировать (не допускается для типа REAL). Если флаг не указан или указан флаг [oberon], то принимается внутреннее соглашение о вызове. [win64] и [systemv] используются для связи с операционной системой и внешними приложениями. При объявлении типов-записей, после ключевого слова RECORD может быть указан флаг [noalign]. Флаг [noalign] означает отсутствие выравнивания полей записи. Записи с системным флагом не могут иметь базовый тип и не могут быть базовыми типами для других записей. Для использования системных флагов, требуется импортировать SYSTEM. ------------------------------------------------------------------------------ Оператор CASE Синтаксис оператора CASE: CaseStatement = CASE Expression OF Case {"|" Case} [ELSE StatementSequence] END. Case = [CaseLabelList ":" StatementSequence]. CaseLabelList = CaseLabels {"," CaseLabels}. CaseLabels = ConstExpression [".." ConstExpression]. Например: CASE x OF |-1: DoSomething1 | 1: DoSomething2 | 0: DoSomething3 ELSE DoSomething4 END В метках вариантов можно использовать константные выражения, ветка ELSE необязательна. Если значение x не соответствует ни одному варианту и ELSE отсутствует, то программа прерывается с ошибкой времени выполнения. ------------------------------------------------------------------------------ Тип WCHAR Тип WCHAR добавлен в язык для удобной поддежки юникода. Для типов WCHAR и ARRAY OF WCHAR допускаются все те же операции, как для типов CHAR и ARRAY OF CHAR, за исключением встроенной процедуры CHR, которая возвращает только тип CHAR. Для получения значения типа WCHAR, следует использовать процедуру WCHR вместо CHR. Для правильной работы с типом, необходимо сохранять исходный код в кодировке UTF-8 с BOM. ------------------------------------------------------------------------------ Конкатенация строковых и символьных констант Допускается конкатенация ("+") константных строк и символов типа CHAR: str = CHR(39) + "string" + CHR(39); (* str = "'string'" *) newline = 0DX + 0AX; ------------------------------------------------------------------------------ Проверка и охрана типа нулевого указателя Оригинальное сообщение о языке не определяет поведение программы при выполнении охраны p(T) и проверки типа p IS T при p = NIL. Во многих Oberon-реализациях выполнение такой операции приводит к ошибке времени выполнения. В данной реализации охрана типа нулевого указателя не приводит к ошибке, а проверка типа дает результат FALSE. В ряде случаев это позволяет значительно сократить частоту применения охраны типа. ------------------------------------------------------------------------------ Дополнительные стандартные процедуры DISPOSE (VAR v: любой_указатель) Освобождает память, выделенную процедурой NEW для динамической переменной v^, и присваивает переменной v значение NIL. COPY (x: ARRAY OF CHAR/WCHAR; VAR v: ARRAY OF CHAR/WCHAR); v := x; Если LEN(v) < LEN(x), то строка x будет скопирована не полностью LSR (x, n: INTEGER): INTEGER Логический сдвиг x на n бит вправо. MIN (a, b: INTEGER): INTEGER Минимум из двух значений. MAX (a, b: INTEGER): INTEGER Максимум из двух значений. BITS (x: INTEGER): SET Интерпретирует x как значение типа SET. Выполняется на этапе компиляции. LENGTH (s: ARRAY OF CHAR/WCHAR): INTEGER Длина 0X-завершенной строки s, без учета символа 0X. Если символ 0X отсутствует, функция возвращает длину массива s. s не может быть константой. WCHR (n: INTEGER): WCHAR Преобразование типа, аналогично CHR(n: INTEGER): CHAR ------------------------------------------------------------------------------ Импорт модулей с указанием пути и имени файла Примеры: IMPORT Math IN "./lib/math.ob07"; (* относительно текущего модуля *) IMPORT M1 IN "C:\lib\math.ob07"; (* абсолютный путь *) ------------------------------------------------------------------------------ Импортированные процедуры Синтаксис импорта: PROCEDURE [callconv, library, function] proc_name (FormalParam): Type; - callconv -- соглашение о вызове - library -- имя файла динамической библиотеки (строковая константа) - function -- имя импортируемой процедуры (строковая константа), если указана пустая строка, то имя процедуры = proc_name например: PROCEDURE [windows, "kernel32.dll", "ExitProcess"] exit (code: INTEGER); PROCEDURE [windows, "kernel32.dll", ""] GetTickCount (): INTEGER; В конце объявления может быть добавлено (необязательно) "END proc_name;" Объявления импортированных процедур должны располагаться в глобальной области видимости модуля после объявления переменных, вместе с объявлением "обычных" процедур, от которых импортированные отличаются только отсутствием тела процедуры. В остальном, к таким процедурам применимы те же правила: их можно вызвать, присвоить процедурной переменной или получить адрес. Так как импортированная процедура всегда имеет явное указание соглашения о вызове, то совместимый процедурный тип тоже должен быть объявлен с указанием соглашения о вызове: VAR ExitProcess: PROCEDURE [windows] (code: INTEGER); Для Linux, импортированные процедуры не реализованы. ------------------------------------------------------------------------------ Скрытые параметры процедур Некоторые процедуры могут иметь скрытые параметры, они отсутствуют в списке формальных параметров, но учитываются компилятором при трансляции вызовов. Это возможно в следующих случаях: 1. Процедура имеет формальный параметр открытый массив: PROCEDURE Proc (x: ARRAY OF ARRAY OF REAL); Вызов транслируется так: Proc(LEN(x), LEN(x[0]), SYSTEM.ADR(x)) 2. Процедура имеет формальный параметр-переменную типа RECORD: PROCEDURE Proc (VAR x: Rec); Вызов транслируется так: Proc(SYSTEM.TYPEID(Rec), SYSTEM.ADR(x)) Скрытые параметры необходимо учитывать при связи с внешними приложениями. ------------------------------------------------------------------------------ Модуль RTL Все программы неявно используют модуль RTL. Компилятор транслирует некоторые операции (проверка и охрана типа, сравнение строк, сообщения об ошибках времени выполнения и др.) как вызовы процедур этого модуля. Не следует вызывать эти процедуры явно. Сообщения об ошибках времени выполнения выводятся в диалоговых окнах (Windows), в терминал (Linux). ------------------------------------------------------------------------------ Модуль API Существуют несколько реализаций модуля API (для различных ОС). Как и модуль RTL, модуль API не предназначен для прямого использования. Он обеспечивает связь RTL с ОС. ------------------------------------------------------------------------------ Генерация исполняемых файлов DLL Разрешается экспортировать только процедуры. Для этого, процедура должна находиться в главном модуле программы, ее имя должно быть отмечено символом экспорта ("*") и должно быть указано соглашение о вызове. Нельзя экспортировать процедуры, которые импортированы из других dll-библиотек.