Продвинутый векторный транслятор — документация, страница 1

Об этом документе

Этот документ описывает возможности языка программирования Продвинутый векторный транслятор (сокращённо: ПВТ) и соответствующего компилятора.

Что такое Продвинутый векторный транслятор?

ПВТ — процедурный императивный структурированный язык программирования с сильной типизацией данных. Объектно-ориентированные возможности как таковые отсутствуют, но могут быть сымитированы.

Генерирует 16-, 32- или 64-битный читабельный ассемблерный код для fasm по выбору программиста. Полученный код может быть использован для создания программ под DOS, GNU/Linux, KolibriOS, MacOS, Windows и другие операционные системы для процессоров x86, а также для создания ядер операционных систем. Весь 64-битный код, сгенерированный ПВТ, является позиционно-независимым.

ПВТ не является оптимизирующим компилятором: он использует регистровый стек для хранения задействованных регистров процессора.

Ограничения

Максимальный размер 16-битной программы составляет примерно 47 килобайт. Однако этот размер носит рекомендательный характер: если вы не используете кучу, которая должна располагаться в том же сегменте памяти, что и программный код, то 16-битная программа может занять весь 64-килобайтный сегмент памяти. Тем не менее, стек (или стеки, если речь идёт о многопоточных 16-битных приложениях) может располагаться в другом 64-килобайтном сегменте памяти.

Имеются ограничения на использование тех или иных типов данных и операций над данными в 16- и 32-битных программах, о чём будет сказано в разделе «Типы данных» и «Выражения».

Лексика

Идентификаторы могут состоять из букв латинского алфавита (A—Z, a—z), символа нижнего подчёркивания «_» и цифр (0—9), причём первым символом не должна быть цифра. Регистр букв учитывается, поэтому идентификаторы, к примеру, getWindowTop и getwindowtop будут означать разные элементы программы.

Целые числа записываются в десятичной или шестнадцатеричной системе счисления, причём если используется шестнадцатеричная запись, то она начинается с префикса 0x или 0X. Примеры: 7609, 0x1db9.

По умолчанию целые числа имеют тип short или int и вы не можете написать целое число, значение которого выходит за рамки этих типов. Чтобы написать целое число типа long, нужно завершить запись суффиксом L или l. Примеры: 0xbf207819000fL, 1000000000000L. Строчная латинская буква «l» не рекомендуется, поскольку её легко спутать с единицей.

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

76.09
100.
.5

Возможна и научная запись. Для разделения мантиссы и порядка используется латинская буква E или e. Например, чтобы записать число 8,85419·10–12 следует написать 8.85419e-12.

По умолчанию действительные числа имеют тип real и вы не можете присвоить их выражениям типа float и double. Чтобы число стало типа float, его необходимо дополнить суффиксом F или f, а для чисел типа double используются суффиксы D и d. Суффиксы R и r используются для чисел типа real и автоматически применяется, если нет других суффиксов. Примеры:

76.09f
100d
0.5r

Символьные литералы всегда записываются в одинарных кавычках. Например: 'u'. Так же возможна запись так называемых escape-последовательностей. Некоторые символы могут быть записаны только с использованием escape-последовательностей. Escape-последовательность начинается с обратной косой черты «\», после которой должен идти один из следующих символов:

0 (означает символ с кодом 0x0000)
b (означает символ с кодом 0x0008)
t (означает символ с кодом 0x0009)
n (означает символ с кодом 0x000a)
f (означает символ с кодом 0x000c)
r (означает символ с кодом 0x000d)
\ (означает символ с кодом 0x005c)
" (означает символ с кодом 0x0022)
' (означает символ с кодом 0x0027)
uXXXX (означает символ с кодом 0xXXXX)

Вместо символов XXXX следует писать шестнадцатеричный код символа в виде четырёх шестнадцатеричных цифр. Например: запись '\u0411' означает то же самое, что и 'Б'.

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

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

"" (пустая строка)
"string"
"АБВ"
"\u0410\u0411\u0412" (то же самое, что и "АБВ")
"длинная\u0020\
строка" (то же самое, что и "длинная строка")

Строковые литералы всегда имеют тип char[]. Операции конкатенации нет в ПВТ, поэтому длинные строки можно записывать только как в последнем примере.

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

.,:;?{}[]()+-*/%~!&|^<=>@#

Комментарии начинаются с последовательности /* и заканчиваются */.

Ключевые слова

Следующие идентификаторы зарезервированы и могут быть использованы только по своему прямому назначению:

assembler
boolean
break
byte
case
catch
char
const
continue
default
dispose
do
double
else
exception
false
finalization
finally
float
for
fvector
if
import
initialization
int
interrupt
long
namespace
new
null
public
pureassembler
real
return
short
struct
switch
throw
true
try
ultra
ultra32
ultra64
void
while
with
xvector
yvector
zvector

Типы данных

Все типы данных в ПВТ поделены на три категории: скалярные, векторные и ссылочные. Среди векторных типов есть составные типы, — те, которые состоят из фиксированного количества значений другого типа. В настоящий момент составными типами являются: ultra и xvector. В этой сводной таблице показаны свойства всех типов.

Название или способ записи Размер в байтах Описание Значение по умолчанию
Скалярные
boolean 1 Переменная принимает только одно из двух значений: false или true.
Доступен: везде
false
char 2 Целое число от 0 до 65535.
Доступен: везде
0
byte 1 Целое число от -128 до 127.
Доступен: везде
0
short 2 Целое число от -32768 до 32767.
Доступен: везде
0
int 4 Целое число от -2147483648 до 2147483647.
Доступен: 32 и 64 бита
0
long 8 Целое число от (-263) до (263-1).
Доступен: только 64 бита
0L
float 4 Действительное число одинарной точности.
Доступен: везде
0F
double 8 Действительное число двойной точности.
Доступен: везде
0D
real 10 Действительное число расширенной точности.
Доступен: везде
0R
Векторные
long 8 4 значения типа short.
Доступен: только 64 бита
0L
ultra 16 4 значения типа int или 8 значений типа short.
Доступен: только 64 бита
new ultra { 0, 0, 0, 0 }
xvector 16 4 значения типа float.
Доступен: только 64 бита
new xvector { 0F, 0F, 0F, 0F }
Ссылочные (так же известные как указательные)
<имя структуры> = Ссылка на структуру в памяти.
Доступен: везде
null
<тип>[] = Ссылка на дескриптор массива.
Доступен: везде
null
<тип>(<тип>, <тип>, …<тип>) = Ссылка на функцию.
Доступен: везде
null

Знак «=» означает совпадение размера с разрядностью программы. Другими словами, в 16-битных программах размер равен двум байтам, в 32-битных программах — четырём байтам, а в 64-битных программах — восьми байтам.

Массивы в ПВТ состоят из дескриптора и содержимого. В дескрипторе хранится длина массива и относительное расположение содержимого в памяти. Поле длины всегда называется length и оно имеет тип int (или short в 16-битных программах).

Структура программы

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

Каждый файл исходного кода может начинаться с перечисления импортируемых модулей. Модуль System импортируется автоматически — повторная попытка его импорта вызывает ошибку компиляции.

Далее следуют один или несколько модулей. Сами модули могут содержать следующие элементы:

Здесь показана структура файла исходного кода на ПВТ:

import <модуль 1>;
import <модуль 2>;
…
import <модуль N>;

namespace <имя модуля>
{
    <константы>
    <структуры>
    <глобальные переменные>
    <исключения>
    <функции>
}

Объявление каждого элемента модуля может начинаться с ключевого слова public, которое сделает его доступным для других модулей. Если же у какого-нибудь элемента модуля нет ключевого слова public, то такой элемент станет доступным только внутри того модуля, в котором находится.

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

Константы

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

public const int MIN_RADIX = 2;
public const int MAX_RADIX = 36;
public const int MIN_BYTE = 0xffffff80; /* = -128 */
public const int MAX_BYTE = 0x0000007f; /* = 127 */
public const int MIN_SHORT = 0xffff8000; /* = -65536 */
public const int MAX_SHORT = 0x00007fff; /* = 65535 */
public const int MIN_INT = 0x80000000; /* -2147483648 */
public const int MAX_INT = 0x7fffffff; /* 2147483647 */
public const long MIN_LONG = 1L << 63;
public const long MAX_LONG = MIN_LONG - 1L;
const ultra LONG_QMULHS_CONST_1 = new ultra { 1, 1, 1, 1 };
const ultra ULTRA_QMULL_CONST_1 = new ultra { -1, 0, -1, 0 };
const ultra ULTRA_QMULL_CONST_2 = ----ULTRA_QMULL_CONST_1; /* new ultra { 1, 0, 1, 0 } */
const ultra ULTRA_QMULH_CONST_1 = new ultra { 0, -1, 0, -1 };
const ultra ULTRA_QMULH_CONST_2 = LONG_QMULHS_CONST_1 ++++ ULTRA_QMULL_CONST_2 ---- new ultra { 0, 0, -2, 2 }; /* new ultra { 2, 1, 4, -1 } */
const ultra ULTRA_OMULHS_CONST_1 = new ultra { ULTRA_QMULH_CONST_1[1], LONG_QMULHS_CONST_1[0], MIN_BYTE >> 4, -ULTRA_QMULH_CONST_2[3] }; /* new ultra { -1, 1, -8, 1 } */
const float MAX_INT_AS_FLOAT = 2.147483648e+9f;
const double MAX_INT_AS_DOUBLE = -(double) MIN_INT; /* 2.147483648e+9d */
const xvector MAX_INT_AS_XVECTOR = new xvector { MAX_INT_AS_FLOAT, MAX_INT_AS_FLOAT, MAX_INT_AS_FLOAT, MAX_INT_AS_FLOAT }; /* new xvector { 2.147483648e+9f, 2.147483648e+9f, 2.147483648e+9f, 2.147483648e+9f } */