Kite-logo
Kite-logo

Я Сделал Это

Почему вообще

Всё началось с мечты — написать свой собственный язык программирования. Я смотрел кучу роликов на YouTube, пытался что-то понять, но эти жалкие попытки ни к чему не приводили. В теории я знал как устроен интерпретатор — лексер, парсер, AST. Но как воплотить это в реальный код — не понимал совсем.

Потом я наткнулся на один ролик который дал мне надежду. Я вспомнил что DeepSeek неплохо пишет код и решил попробовать — первый Python проект заработал. Я был на седьмом небе от счастья.

Но тот проект был сырой и недоделанный. А когда пытаешься сделать на нём что-то серьёзное — DeepSeek выдавал код полный ошибок. Я фиксил один баг, появлялись три новых. Стало ясно что просто просить AI написать язык — не работает.

Однажды я снова сидел на YouTube и наткнулся на видео с канала WeaklyHow — он создавал свой язык программирования с помощью нейросетей. Видео было на английском, но я смотрел просто так — контекст был понятен и без перевода.

И вот в этом видео была нейросеть которая меня зацепила — Claude AI. Я открыл его в браузере, описал что хочу, и попытался создать проект. Собрал в Termux — и он заработал. Без лагов, без багов, с первого раза.

Я не мог поверить. После всех мучений с DeepSeek это было как день и ночь.

Но это был ещё не KITE.

После глотка свежего воздуха с Claude AI я стал пробовать разные подходы — компиляцию и интерпретацию, функциональное программирование. Всё это я уже делал на C, для лучшей производительности и скорости.

Я устроил настоящий марафон в поисках лучшего синтаксиса и функционала для полноценного проекта. Пробовал, выбрасывал, начинал заново. Искал то самое сочетание — минимальный синтаксис, максимум возможностей.

И вот 21 марта 2026 года он родился — KITE.

По факту я его вайбкодил — но KITE не коммерческий проект. Вы можете делать с ним что душе угодно: сделать свой язык на его основе, улучшить его, или наоборот сломать всё что я построил. Исходники открытые, MIT лицензия — берите и творите.


Синтаксис — осознанные решения

Первое что бросается в глаза — KITE не похож ни на C, ни на Python, ни на Rust.

set name = "мир"
say("Привет, ${name}!")

def factorial(n):
    when n <= 1: give 1 end
    give n * factorial(n - 1)
end

loop for i in range(10):
    say("${i}! = ${factorial(i)}")
end

Несколько осознанных решений:

set вместо var/let — короче, читается как "установить значение". Переприсваивание без set вызывает ошибку — нельзя случайно создать переменную опечаткой.

when/orwhen/else вместо if/elif/elsewhen читается естественно: "когда x > 0 — делай это".

give вместо return — возвращаешь значение, "отдаёшь" его.

loop while и loop for — все циклы начинаются одинаково, нет путаницы.

Нет точек с запятой — блоки через двоеточие и end, как в Ruby.


Как устроен интерпретатор

Классическая трёхэтапная архитектура: лексер → парсер → интерпретатор.

Лексер (lexer.c)

Лексер превращает исходный текст в поток токенов. Например:

set x = 42 + 8

Превращается в:

TK_SET  TK_IDENT("x")  TK_ASSIGN  TK_NUM(42)  TK_PLUS  TK_NUM(8)

Интересная деталь — интерполяция строк ${} обрабатывается прямо в лексере. Строка "Привет, ${name}!" разбивается на маркеры внутри токена TK_FMTSTR. Потом парсер разбирает выражение внутри ${} как обычный под-лексер.

Парсер (parser.c)

Рекурсивный descent-парсер строит AST (Abstract Syntax Tree). Каждый узел дерева — структура Node с полем kind:

typedef enum {
    ND_NUM, ND_STR, ND_FMTSTR, ND_BOOL, ND_NIL,
    ND_SET, ND_ASSIGN, ND_BINOP, ND_UNOP,
    ND_CALL, ND_INDEX, ND_PROP,
    ND_DEF, ND_WHEN, ND_LOOP_WHILE, ND_LOOP_FOR,
    ND_DO, ND_OBJ, ND_GIVE, ND_BREAK, ND_NEXT,
    /* ... */
} NKind;

Для выражений используется классическая иерархия приоритетов:

assign → or → and → not → cmp → add → mul → pow → unary → postfix → primary

Интерпретатор (interp.c)

Tree-walking интерпретатор — просто рекурсивно обходит AST:

Value *eval(Interp *ip, Node *n, Env *env) {
    switch (n->kind) {
        case ND_NUM:  return val_num(n->num);
        case ND_SET: {
            Value *v = eval(ip, n->set.val, env);
            env_def(env, n->set.name, v);
            return val_nil();
        }
        /* ... */
    }
}

Управление памятью

Вместо GC — reference counting. Каждое значение имеет счётчик ссылок:

struct Value {
    VType type;
    int   refs;   /* счётчик ссылок */
    union { double num; char *str; KiteList *list; /* ... */ };
};

val_ref() увеличивает счётчик, val_unref() уменьшает — и освобождает если достиг нуля. Просто и предсказуемо, без пауз сборщика мусора.


Замыкания

Замыкания в KITE работают через лексическое окружение. Каждая функция хранит ссылку на Env где была создана:

typedef struct KiteFn {
    char  *name;
    char **params;
    int    nparams;
    Node  *body;
    Env   *closure;  /* ← захваченное окружение */
    int    refs;
} KiteFn;
def make_counter(start):
    set count = start
    def inc():
        count += 1
        give count
    end
    give inc
end

set c = make_counter(0)
say(c())   # 1
say(c())   # 2
say(c())   # 3

ООП

ООП реализован через ключевое слово obj. Под капотом — структура KiteClass с env методов и KiteInstance с env полей:

obj Animal:
    set name = nil

    def init(self, name):
        self.name = name
    end

    def speak(self):
        say("${self.name} издаёт звук")
    end
end

obj Dog extends Animal:
    set _tricks = 0          # приватное поле (_)

    def learn(self):
        self._tricks += 1
    end

    def speak(self):
        super.speak(self)    # вызов родителя
        say("${self.name}: Гав!")
    end
end

set d = Dog.new("Шарик")
d.learn()
d.speak()
say(d is Dog)       # true
say(d is Animal)    # true

Инкапсуляция — поля с _ в начале доступны только внутри методов класса. Попытка обратиться снаружи бросает AccessError.


Обработка ошибок

do:
    set data = file_read("config.txt")
err IOError:
    say("Файл не найден, создаём...")
    file_create("config.txt")
err:
    say("Неожиданная ошибка: ${err_msg}")
end

Типы ошибок: ZeroDivisionError, NameError, IndexError, TypeError, IOError, AccessError, RuntimeError.


Стандартные библиотеки

use math      # tan, log2, clamp, lerp, E, INF...
use rand      # rand(), rand_choice(), rand_shuffle()
use string    # str_pad_left, str_count, str_rev...
use list      # list_sum, list_zip, list_group, list_any...
use io        # file_read, file_write, file_lines...
use os        # os_env, os_shell, os_time...

Запуск на Android (Termux)

Это, пожалуй, самая приятная часть. KITE компилируется и работает прямо на телефоне:

pkg install clang make
tar -xzf kite-lang.tar.gz
cd kite-lang
make CC=clang
cp kite $PREFIX/bin/kite
kite --version   # kite 1.2.0

После этого можно писать программы в micro или vim прямо на телефоне. Подсветка синтаксиса для micro тоже есть в репозитории.


Что внутри (статистика)

Файл

Строк

Роль

kite.h

~250

Типы, AST, прототипы

lexer.c

~200

Токенизатор

parser.c

~700

Рекурсивный парсер

value.c

~300

Значения, refcount, env

interp.c

~1600

Интерпретатор + builtins

main.c

~120

Точка входа, REPL

Итого

~3200


Что дальше

  • Байт-код компилятор и VM вместо tree-walking

  • Модульная система (use mymodule)

  • Многострочные строки

  • Форматированный вывод


Попробовать

GitHub

git clone https://github.com/svetozargashtold7-png/kite-lang
cd kite-lang
make && ./kite

Буду рад вопросам и критике в комментариях. Если захочешь поучаствовать в разработке — PR приветствуются!