English · Русский
A general-purpose programming language whose interpreter compiles the AST to
closures. Written in TypeScript, runs on Node with no build step and zero runtime
dependencies — node src/cli.ts file.sable is the whole toolchain.
fn fib(n) { if n < 2 { return n }; return fib(n - 1) + fib(n - 2) }
print(fib(20))
6765
- Nothing to install. Node executes the TypeScript sources natively;
tscis used for type checking only, and the runtime pulls in no packages at all. - Diagnostics meant to be read. Every error carries a position, the source line with a caret under the culprit, a call stack, and a suggestion when a name looks like a typo. All syntax errors are reported in a single pass.
- Tooling in the box. A formatter (
sable fmt), a static checker that runs without executing the program (sable --check), a VS Code syntax grammar, and two fuzzers that shrink any failure they find down to a few lines. - 536 checks in seven independent suites — including the exact text of every error message, every example program, every code block in the documentation, and the command-line behaviour itself. A full rewrite of the interpreter core passed them without touching a single expected output.
Try it in the browser — no install, runs the same interpreter compiled for the web. Full English documentation — language reference and internals.
node src/cli.ts examples/01_hello.sable # run a file
node src/cli.ts # interactive session
node src/cli.ts --check file.sable # analyse without running
npm test # all ten suitesThe reference, the tutorial and all diagnostics are written in Russian. Start at docs/TUTORIAL.md, the full reference is docs/LANGUAGE.md, the internals are in docs/ARCHITECTURE.md.
Язык программирования общего назначения: лексер → парсер → AST → компиляция в замыкания. Написан на TypeScript, работает на Node без сборки и без единой зависимости в рантайме.
struct Point {
x = 0
y = 0
fn len() { return sqrt(self.x ^ 2 + self.y ^ 2) }
}
let points = [Point(3, 4), Point(1, 1)]
let longest = points.sort((a, b) -> b.len() - a.len()).first()
print("Самая длинная: ${longest.x}, ${longest.y} → ${round(longest.len(), 2)}")
Требуется Node 22.18+ или 24+ — с этих версий TypeScript исполняется нативно без флагов, поэтому компилировать нечего. Проверено прогоном всего набора тестов на 22.18, 24 и 25; на 22.17 и раньше не запустится.
Попробовать, ничего не устанавливая, — песочница в браузере: тот же интерпретатор, собранный под веб. Выполняет, форматирует и проверяет без запуска.
node src/cli.ts examples/01_hello.sable # выполнить файл
node src/cli.ts # интерактивный режим: Tab, история, :помощь
node src/cli.ts -e 'print(2 ^ 10)' # выполнить строку
node src/cli.ts --check файл.sable # проверить, не запуская
node src/cli.ts fmt -w файл.sable # привести к каноническому виду
node bench/run.ts # замеры производительности
npm test # прогнать все тестыЧтобы вызывать просто sable:
npm link # или: ln -s "$PWD/bin/sable.mjs" /usr/local/bin/nur
sable examples/04_shapes.sable| Типы | number, string, bool, nil, list, map, range, fn, структуры |
| Переменные | let (изменяемая), const (нет) |
| Функции | значения по умолчанию, замыкания, первый класс, рекурсия, лямбды x -> x * 2 |
| Структуры | поля со значениями по умолчанию, методы, self |
| Управление | if/else if/else, while, for … in, break, continue, тернарный ? : |
| Ошибки как значения | try/catch, свой бросок через error(…), assert |
| Модули | import "lib/math.sable" as math — файл выполняется один раз, циклы ловятся |
| Строки | вставки ${…}, многострочные в `обратных кавычках`, ~15 методов |
| Коллекции | map/filter/reduce/sort/find, срезы, отрицательные индексы |
| Диагностика | позиция в исходнике, стрелка под виновником, стек вызовов, подсказки по опечаткам, все синтаксические ошибки за один проход |
| Прочее | JSON, файлы, ввод с клавиатуры |
Пошаговое введение — docs/TUTORIAL.md. Полный справочник — docs/LANGUAGE.md. Как устроен интерпретатор — docs/ARCHITECTURE.md.
$ sable pay.sable
Ошибка выполнения: имя «blance» не определено — возможно, имелось в виду «balance»
--> pay.sable:7:22
|
7 | let remaining = blance - amount
| ^
в withdraw (pay.sable:12:16)
в main (pay.sable:18:5)
Каждая ошибка знает: что произошло, где именно, и через какие вызовы туда пришли.
Повторяющиеся кадры рекурсии схлопываются в × N.
src/
lexer.ts текст → лексемы
parser.ts лексемы → AST (рекурсивный спуск)
interpreter.ts AST → замыкания (компиляция и выполнение)
checker.ts AST → замечания (проверка без запуска)
modules.ts загрузка import: кэш, циклы, пути
values.ts значения времени выполнения, равенство, печать
environment.ts области видимости и поиск имён
stdlib.ts встроенные функции и методы типов
format.ts AST → канонический текст (sable fmt)
errors.ts единый формат ошибок со стрелкой
cli.ts запуск файла, REPL, -e, --check, fmt
tests/ golden-тесты: вывод программы сверяется с эталоном
examples/ примеры (их вывод тоже под замком тестов)
selfhost/ Sable, написанный на Sable: лексер, парсер, интерпретатор
docs/ учебник, справочник, устройство
npm test # проверить всё: язык, анализатор, документацию
node tests/run.ts --only=maps # только словари
node tests/run.ts --update # перезаписать эталоны (после осознанной правки)Десять независимых наборов:
| Набор | Что запирает |
|---|---|
tests/run.ts |
вывод программ и текст каждой ошибки, плюс вывод всех примеров |
tests/checker.ts |
статический анализ: каждая проверка «ловит» и «не ложно срабатывает» |
tests/docs.ts |
код из документации выполняется, вывод сверяется с напечатанным |
tests/grammar.ts |
подсветка знает те же слова, что и язык |
tests/format.ts |
форматтер: идемпотентность, сохранение дерева, комментариев и вывода |
tests/cli.ts |
сама команда: коды выхода, потоки, поведение при обрыве канала |
tests/web.ts |
песочница: сборка не тянет ничего из Node, поведение как в терминале |
tests/scale.ts |
посимвольный проход по строке растёт линейно, а не квадратично |
tests/selfhost.ts |
перёд на Sable даёт те же лексемы и то же дерево, что настоящий |
tests/selfrun.ts |
интерпретатор на Sable печатает то же, что настоящий, — на всех примерах |
Отдельно живут два охотника, в npm test они не входят — это охота, а не проверка:
node tests/fuzz.ts— случайные программы: следы JavaScript наружу, зависания, расхождения--checkс выполнением, нарушения законов языка;node tests/fuzz-tools.ts— всё вокруг ядра: наборы модулей (цепочки, ромб, циклы импорта), форматтер как оракул (вывод до и после обязан совпасть), файловые функции,input()и интерактивный режим.
Найденный пример оба сокращают сами — из сотни строк остаётся несколько.
Отдельными кейсами покрыт каждый класс ошибки: неизвестное имя, несовпадение
типов, деление на ноль, выход за границы, запись в const, неверное число
аргументов, вызов не-функции, обращение к полю у nil, синтаксис, незакрытая
строка, глубокая рекурсия, свои ошибки, отсутствующий модуль, try без catch.
Документация с выдуманным выводом хуже отсутствующей — поэтому она под тестом, а не под честным словом.
Сделано после 0.1: модули (import … as), перехват ошибок (try/catch),
статическая проверка (--check), форматтер (sable fmt), расширенная библиотека,
разбор с восстановлением, подсветка для VS Code, фаззеры, песочница в браузере,
английская документация.
Скорость выросла в 3,3 раза: точечные оптимизации по профилю, компиляция AST
в замыкания, разрешение локальных имён в слоты.
Ближайшие шаги, в порядке пользы:
- Целочисленный тип — сейчас всё число с плавающей точкой, как в JS.
- Байткод и стековая ВМ вместо замыканий — следующий крупный шаг по скорости.
- Асинхронность или потоки — сейчас язык строго последовательный.
- Свободные функции для всех методов словаря — сейчас ключ с именем метода закрывает его, а обойти можно только четыре из шестнадцати.
Известное ограничение: глубина рекурсии. Порог языка — 900 вызовов, но реальная
ёмкость зависит от тела функции: от ~1180 вызовов при плоском теле до ~150 при
сорока уровнях вложенности выражений. Глубже — ошибка языка про вложенность
вычислений, не стек JavaScript. Порог поднимается через SABLE_MAX_DEPTH
вместе с node --stack-size=...; цифры и способ замера — в
docs/ARCHITECTURE.md.
Дониёр Ботиров — основатель THE BOTIROFF LLC (США), компании по разработке программного обеспечения, и холдинга BOTIROFF.
Строит продукты, а не демонстрации: системы, которые каждый день работают у живых клиентов и держат их деньги, расписания и переписку.
| Проект | Что это |
|---|---|
| THE CRM | CRM с искусственным интеллектом для учебных центров: финансы, посещаемость, зарплаты, ИИ-обзвон и боты в Telegram и Instagram |
| ULTRATHINK | Премиальный учебный центр по искусственному интеллекту |
| BOTIROFF SPACE | 3D-конфигуратор мебели с подбором через ИИ |
| dbit | Разработка на заказ |
| Sable | Этот язык программирования |
Образование — Высшая школа экономики и Финансовый университет при Правительстве Российской Федерации. Сертификации Meta и Google, стажировки в Газпромбанке и Citibank. IELTS 9.0 — высший возможный балл, SAT 1590 из 1600.
Связь: botiroff.com · t.me/mrdoniyor · github.com/BOTIROFF-D
Код — под MIT, Copyright © 2026 Doniyor Botirov. Форкайте, меняйте, берите в коммерческие проекты; единственное условие — сохранить копирайт.
Лицензия распространяется на исходный код и не даёт прав на название. «Sable» — имя проекта, принадлежащее THE BOTIROFF LLC; знак ™ означает заявленные права на название, регистрация не требуется. Выпускать форк под этим именем или так, чтобы он выглядел официальной версией, нельзя: назовите его своим именем, этого достаточно.
Name. The MIT license covers the source code and grants no rights to the project name. “Sable” is a project name of THE BOTIROFF LLC. Fork and use the code freely — just don't ship your fork under the Sable name, or in a way that implies it is the official version.