Candy - экспериментальный декларативный язык и набор инструментов для описания моделей данных, интеграций, поведения генераторов и кода, зависящего от целевого языка.
package("main");
use (
"github.com/CandyCrafts/plugins/db" -> d,
)
#[d::sqlite::table("User")];
go::model User {
pub (
Id: string = go::lib("github.com/google/uuid")::NewString(),
Name: string = "none",
)
}
Candy находится в активной разработке. Сейчас команда build разбирает и
анализирует исходный файл, но пока не генерирует итоговый код.
- Состояние проекта
- Быстрый старт
- Установка
- Командная строка
- Синтаксис языка
- Полный пример
- Пакеты
- Импорты
- Атрибуты
- Квалифицированные объявления
- Переменные и поля
- Группы публичных объявлений
- Методы
- Типы
- Выражения
- Комментарии и разделители
- Диагностика и языки
- Просмотр дерева через Lipa
- Разработка
- Участие в проекте
- Структура проекта
- Текущие ограничения
| Возможность | Состояние |
|---|---|
| Лексер и токены с позициями | Готово |
| Парсер и AST | Готово |
| Восстановление после синтаксических ошибок | Готово |
| Импорты и псевдонимы | Готово |
| Атрибуты и квалифицированные пути | Готово |
| Глобальные переменные и поля моделей | Готово |
| Базовая проверка типов | Готово |
| Локализованная диагностика | Готово |
| Постоянный кэш переводов | Готово |
| Интерактивный просмотр AST | Готово |
| Генерация кода | В разработке |
| Выполнение плагинов | В разработке |
Команда candy install |
Не реализовано |
git clone https://github.com/CandyCrafts/candy.git
cd candy
make build
./bin/candy build examples/models/1-model.cmПоказать справку:
./bin/candy helpПоказать справку на русском:
./bin/candy -L ru helpОткрыть визуализатор AST:
go run ./cmd/lipa examples/models/1-model.cmДля сборки Candy требуются Go 1.25.6 или новее, Make и компилятор C для
драйвера SQLite.
make buildИсполняемый файл будет создан по пути bin/candy.
make installПо умолчанию Candy устанавливается в ~/.local/bin/candy. Другой каталог можно
указать через INSTALL_DIR:
make install INSTALL_DIR=/usr/local/binЕсли стандартного каталога нет в PATH, добавьте его в настройки оболочки:
export PATH="$HOME/.local/bin:$PATH"go run ./cmd/release help
go run ./cmd/release build examples/models/1-model.cmmake test # запустить тесты
make build-all # собрать артефакты для настроенных платформ
make list-platforms # вывести список платформ
make clean # удалить каталог bin/CLI использует go-sqlite3, поэтому для полноценной кросс-компиляции нужен
подходящий набор инструментов CGO. Текущая команда build-all отключает CGO.
candy [глобальные параметры] <команда> [аргументы]
| Параметр | Описание |
|---|---|
-h, --help |
Показать справку. |
-L <lang>, --lang <lang> |
Выбрать язык вывода. |
--lang=<lang> |
Выбрать язык вывода. |
Примеры:
candy -L ru help
candy --lang=uk help
candy help --lang=rucandy build <путь> [имя]
Путь вычисляется относительно каталога, из которого была запущена Candy. Если имя проекта не указано, используется имя исходного файла без расширения.
candy build examples/models/1-model.cm
candy build src/user.cm usersСейчас команда читает файл, разбирает его, запускает базовый анализ и завершается с ошибкой при наличии критических диагностик. Выходные файлы пока не генерируются.
candy help
candy --lang ru helpКоманда candy install зарезервирована для будущей установки пакетов и
инструментов. Для установки исполняемого файла Candy используйте make install.
Исходный файл может содержать:
- объявление
package(...); - импорты
use (...); - глобальные атрибуты;
- объявления
letиpub let; - группы публичных объявлений;
- квалифицированные объявления, например
go::model User; - поля, атрибуты и методы внутри объявлений;
- литералы, арифметические выражения, идентификаторы и квалифицированные вызовы.
Названия model, impl, sqlite и table являются сегментами пути, а не
фиксированными ключевыми словами. Благодаря этому плагины и генераторы могут
создавать собственные пути без расширения основной грамматики.
package("main");
use (
"github.com/CandyCrafts/plugins/db" -> d,
"github.com/CandyCrafts/plugins/std",
)
#[lang=custom("github.com/CandyCrafts/LangEngines/Go@latest")];
#[d::sqlite::table("User")];
go::model User {
#[db::sqlite::index];
pub (
Id: string = go::lib("github.com/google/uuid")::NewString(),
Name: string = "none",
Enabled: bool = true,
)
}
#[composer::file::no_edit(true)];
go::impl User {
#[d::go::func::delete_rec];
pub banned() -> go::type::error,
}
package("main");
В объявлении package допускается только один сегмент пути и список аргументов.
Более длинная форма, например package::nested(...), считается ошибкой.
Импорты объединяются внутри use (...):
use (
"github.com/CandyCrafts/plugins/db",
"github.com/CandyCrafts/plugins/http" -> http,
)
Оператор -> назначает импорту псевдоним. После последнего импорта можно
оставить запятую.
use (
"github.com/CandyCrafts/plugins/database" -> db,
)
Атрибуты записываются внутри #[...] и могут находиться на верхнем уровне или
внутри тела объявления:
#[db::sqlite::table("User")];
#[db::sqlite::index];
#[lang=custom("github.com/CandyCrafts/LangEngines/Go@latest")];
В одном блоке можно указать несколько атрибутов:
#[db::sqlite::index, cache::enabled(true)];
Позиционные аргументы:
#[database::column("name")];
Именованные аргументы:
#[database::column("name", nullable: false)];
Вложенные квалифицированные вызовы:
#[db::sqlite(db::std::name())];
Атрибуты с присваиванием подходят для настроек:
#[lang=custom("github.com/CandyCrafts/LangEngines/Go@latest")];
Квалифицированное объявление состоит из пути, имени и тела:
go::model User {
}
go::impl User {
}
custom::backend::entity Order {
}
У пути нет обязательного последнего слова. Парсер не обрабатывает go::model
особым образом. Пути из одного сегмента также поддерживаются:
model User {
}
Глобальные переменные объявляются через let или pub let:
let port: int = 8080;
pub let name: string = "Candy";
let enabled = true;
let title: string;
У переменной должен быть тип, начальное значение или оба элемента.
У полей внутри квалифицированного объявления слово let можно опустить:
go::model User {
pub Id: string = "none",
let Internal: bool = false,
Name: string = "unknown",
}
Форма pub let внутри тела также поддерживается:
go::model User {
pub let Id: string = "none",
}
На верхнем уровне элементы публичной группы записываются с let:
pub (
let host: string = "localhost",
let port: int = 8080,
)
Внутри квалифицированного объявления слово let можно не указывать:
go::model User {
pub (
Id: string = "none",
Name: string = "unknown",
)
}
Одиночные формы продолжают поддерживаться:
pub let globalName: string = "Candy";
go::model User {
pub Id: string = "none",
}
Методы объявляются внутри квалифицированных тел:
go::impl User {
pub save(),
pub banned() -> go::type::error,
pub validate() -> (go::type::error, go::type::error),
}
Возвращаемый тип необязателен. Один тип указывается сразу после ->, а несколько
типов записываются через запятую в круглых скобках. Типизированные параметры
функций пока не реализованы: аргументы метода используют тот же синтаксис
вызовов, что и аргументы атрибутов.
Типы записываются как пути из идентификаторов без круглых скобок:
string
go::type::error
project::domain::UserID
Учитывается весь путь. Текущий анализатор считает следующие типы разными:
string
go::type::string
// однострочный комментарий
/* блочный комментарий */
В конце объявлений верхнего уровня рекомендуется ставить точку с запятой:
package("main");
#[db::sqlite::table("User")];
Если граница конструкции однозначна, отсутствие точки с запятой создаёт предупреждение, а не критическую ошибку. Запятые разделяют импорты, аргументы, поля и сгруппированные объявления.
Диагностика Candy содержит положение в исходном коде, уровень важности, подсвеченный диапазон и локализованное описание. После некорректной конструкции парсер старается продолжить работу, чтобы за один запуск показать несколько независимых ошибок.
Основной язык - английский. Для текущих диагностик также добавлены переводы на русский. Текст CLI без готового перевода может переводиться автоматически:
candy -L ru help
candy -L uk helpАвтоматические переводы кэшируются в памяти и SQLite. Постоянный кэш хранится в пользовательском каталоге настроек операционной системы:
<каталог-настроек-пользователя>/Candy/candy.sqlite
Если автоматический перевод завершился ошибкой или указан некорректный код языка, Candy использует исходный текст.
Запустить тесты основного модуля:
make testЗапустить тесты отдельных пакетов:
go test ./internal/parser
go test ./internal/analyzerУ независимых модулей собственные наборы тестов:
(cd pkg/lipa && go test ./...)
(cd pkg/digreyt && go test ./...)Модели базы данных и SQL-запросы генерируются из
internal/database/cache.rpl. Файлы с пометкой Code generated by RPL. DO NOT EDIT. необходимо перегенерировать, а не изменять вручную.
Правила оформления изменений, тестирования и работы с RPL описаны в
CONTRIBUTING.md. Для генерации базы данных проект использует
RPL 0.7.2.
buildпока не генерирует исходный код или исполняемые файлы.- Команда
candy installне реализована. - Сейчас проект загружает только один исходный файл.
- Вывод типов ограничен литералами и простыми арифметическими выражениями.
- Возвращаемые типы квалифицированных вызовов не выводятся.
- Типизированные параметры методов не реализованы.
- Псевдонимы импортов разбираются, но пока не используются анализатором.
- Значение атрибутов пока не проверяется по схемам плагинов.
- Внешний код не может импортировать внутренний API парсера Candy.
- Для кросс-компиляции версии с SQLite требуется дополнительная настройка CGO.
- До стабильного выпуска совместимость синтаксиса не гарантируется.