Исполняемое ядро 0.0.4
Это спецификация текущего прототипа, не полного языка Liza.
Типы и литералы
Доступны u8, u16, u32, u64, i8, i16, i32, i64 и bool.
Для N бит unsigned имеет диапазон 0…2^N−1, signed — −2^(N−1)…2^(N−1)−1.
Знаковые битовые операции определены через дополнение до двух.
bool содержит только true и false; неявных преобразований между типами нет.
Литерал хранится компилятором как математическое целое и получает тип из
аннотации привязки, результата функции, параметра вызова или типизированного
операнда. Контекст распространяется через скобки и арифметические операции
в обе стороны. Это вывод типов, а не изменение порядка исполнения.
Если контекста нет, выбирается u32, независимо от величины литерала.
Литерал вне диапазона выбранного типа — ошибка компиляции.
fn offset(x: i16) -> i16 {
y = (1 + 2) + x // все операнды имеют тип i16
return y
}
fn main() -> u32 {
minimum: i8 = -128
positive: u8 = 42
if minimum < 0 && positive == 42 { return 0 }
return 1
}
Минус непосредственно перед числовым литералом учитывается до проверки
диапазона: -128 допустим для i8. Унарный минус перед выражением требует
знакового типа и проверяет переполнение: -MIN вызывает trap.
-(128) в контексте i8 отклоняется, поскольку внутренний литерал 128
не представим; для минимального значения используется запись -128.
Отрицательный литерал без знакового контекста отклоняется как непредставимый
в u32. Десятичные и 0x-литералы подчиняются одинаковым правилам диапазона.
Синтаксис
program := function*
function := "fn" name "(" parameters? ")" "->" type (":" "pure")? block
type := "u8" | "u16" | "u32" | "u64" | "i8" | "i16" | "i32" | "i64" | "bool"
parameters := name ":" type ("," name ":" type)*
block := "{" statements "}"
statement := name (":" type)? "=" expression | "return" expression | conditional
conditional := "if" expression block ("else" (block | conditional))?
expression := literal | "true" | "false" | name | name "(" arguments? ")"
| "(" expression ")" | unary expression | expression binary expression
unary := "!" | "~" | "-"
binary := "+" | "-" | "*" | "/" | "%" | "+%" | "-%" | "*%"
| "==" | "!=" | "<" | "<=" | ">" | ">="
| "&" | "|" | "^" | "<<" | ">>" | "&&" | "||"
Привязки и return разделяются newline или ;, перед } разделитель
необязателен. После блока if/else разделитель также необязателен.
else допускается на той же или следующей строке; ; перед else недопустим.
Заголовки функций и выражения пока однострочные. Открывающая { следует
на той же строке, что заголовок функции, условие или else.
Комментарий // продолжается до конца строки.
Идентификаторы ASCII: [A-Za-z_][A-Za-z_0-9]*. Имена типов, fn, return,
pure, true, false, if, else зарезервированы.
Имена, функции и ветвления
Привязки неизменяемые: повторное объявление и затенение видимого имени запрещены.
Имя доступно после объявления. Ветка видит внешние имена, но её локальные имена
не доступны соседней ветке и коду после if. Новое имя можно независимо
использовать в соседних ветках или после завершения его области.
Функции видимы во всём модуле, включая forward calls. Рекурсия разрешена,
завершение не доказывается. Имена функций и локальных значений находятся
в разных пространствах: f(...) ищет функцию, простое f — значение.
Типы параметров и результата обязательны.
if — инструкция. Условие вычисляется один раз и имеет тип bool;
выполняется только выбранная ветка. else необязательно; доступны вложенные
ветвления и else if. Каждый путь функции должен закончиться return
объявленного типа. Возврат из обеих веток достаточен; код после него отклоняется.
Проверка структурная: if true {return 1} без else требует последующего
возврата. Обе ветки проверяются статически, даже если условие известно заранее.
Ровно одна main() -> u32 без параметров обязательна. : pure необязательно:
все доступные конструкции входят в чистое ядро. Это ещё не полная система эффектов.
Порядок вычислений и приоритеты
Операнды и аргументы вычисляются слева направо, инструкции — в порядке исходника.
&& вычисляет правую часть только если левая истинна; || — только если ложна.
Обе части имеют тип bool и всегда проверяются статически.
Приоритеты от высокого к низкому:
| Операции | Операнды | Результат |
|---|---|---|
!, ~, унарный - |
bool / целое / знаковое целое соответственно | Тип операнда |
*, *%, /, % |
Одинаковый целый тип | Тот же тип |
+, -, +%, -% |
Одинаковый целый тип | Тот же тип |
<<, >> |
Целое слева, u32 справа |
Тип слева |
<, <=, >, >= |
Одинаковый целый тип | bool |
==, != |
Одинаковый скалярный тип | bool |
& |
Одинаковый целый тип | Тот же тип |
^ |
Одинаковый целый тип | Тот же тип |
\| |
Одинаковый целый тип | Тот же тип |
&& |
bool, bool | bool |
\|\| |
bool, bool | bool |
Бинарные операции одного приоритета ассоциируются слева. Для проверки маски
нужны скобки: (flags & mask) != 0. Числовые операнды сравнения не получают
тип bool из результата: их тип выводится независимо, либо выбирается u32.
Сравнения учитывают знаковость: для i8 −1 < 1, для u8 255 > 1.
Арифметика и биты
+, -, * и унарное отрицание signed проверяют представимость результата;
при переполнении выполняется trap. +%, -%, *% считают по модулю 2^N;
для signed результат интерпретируется в дополнительном коде.
Промежуточное переполнение не исчезает при свёртке выражения: (x+1)-1
вызывает trap для MAX даже при неиспользуемом результате.
/ и % требуют одинакового целого типа операндов и возвращают тот же тип.
Частное округляется к нулю; ненулевой остаток имеет знак делимого.
Например, для i32: -7 / 3 == -2, -7 % 3 == -1, 7 % -3 == 1.
Математически a = q*b + r, где q — частное, r — остаток.
Обе операции вызывают trap при нулевом делителе. Для signed MIN / -1
вызывает trap, а MIN % -1 возвращает ноль. Остаток вычисляется самостоятельно,
без промежуточного checked-частного. Эти правила действуют и для констант:
1 / 0 типизируется, но вызывает trap только при исполнении.
&, |, ^, ~ работают с N битами без арифметического переполнения.
<< отбрасывает выходящие биты, затем интерпретирует результат в исходном типе.
>> заполняет старшие биты нулями для unsigned и знаковым битом для signed.
Счётчик — u32; n >= N вызывает trap до машинного сдвига. Его значение
не маскируется и не усекается перед проверкой. Signed-счётчик и отрицательный
литерал счётчика отклоняются. Ноль — допустимый сдвиг.
Trap — наблюдаемое аварийное завершение. Оптимизация должна сохранять наблюдаемый результат, включая обязательные trap и отсутствие исполнения невыбранных ветвей/операндов. Перенос проверки допускается только с доказательством эквивалентности. Конкретный сигнал и сообщение не гарантируются. Исчерпание памяти/стека не моделируется ядром. Чистота не доказывает отсутствие trap.
main передаёт результат host ABI как код завершения. POSIX shell видит
младшие 8 бит. Для проверки значений шире 8 бит нужно сравнение полных значений,
а не только кода завершения процесса.
Liza IR и backend
IR версии 5 содержит типизированные значения, параметры, базовые блоки,
return, branch, jump и phi для результатов короткого замыкания.
Типы сохраняют ширину и знаковость. Арифметическая политика задаётся overflow,
политика неправильного счётчика — shift_policy: trap.
div и rem имеют division_policy: trap: нулевой делитель вызывает trap;
для signed div дополнительно проверяется MIN / -1. Частное округляется
к нулю, signed rem возвращает ноль для MIN % -1.
may_trap у операций и функций распространяется через вызовы, включая рекурсию.
Это консервативная оценка: она не доказывает достижимость trap, завершение функции
или отсутствие исчерпания стека. После изменения IR сведения пересчитываются.
Порядок операций и граф блоков сохраняют порядок потенциальных trap.
Проверки типов, имён, диапазонов и возврата выполняются до backend.
JSON доступен через emit-ir; формат нестабилен, внешнего ввода IR пока нет.
LLVM backend использует i1 для bool и iN для N-битных целых, signed/unsigned
сравнения и overflow intrinsics, обычную модульную арифметику без nsw/nuw,
условный переход к trap перед сдвигом. Проверка счётчика остаётся 32-битной,
даже если сдвигаемое значение уже. Деление проверяет ноль и signed-переполнение
до LLVM-инструкции. Для signed-остатка делитель −1 заменяется на +1 до srem,
сохраняя нулевой результат и исключая неопределённый случай MIN % -1.
Оптимизацией занимается Clang/LLVM.
Ещё не реализовано
Насыщение, числовые преобразования, массивы, изменяемые привязки, циклы, модули и I/O. Проекты будущих числовых операций находятся в числовой модели.