Skip to content

CandyCrafts/candy

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

42 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Candy

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 разбирает и анализирует исходный файл, но пока не генерирует итоговый код.

Содержание

Состояние проекта

Возможность Состояние
Лексер и токены с позициями Готово
Парсер и 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.

Установка в PATH

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.cm

Другие команды Make

make 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=ru

Сборка

candy 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.
  • До стабильного выпуска совместимость синтаксиса не гарантируется.

About

Declarative language for describing models and metadata.

Topics

Resources

Contributing

Stars

3 stars

Watchers

0 watching

Forks

Releases

No releases published