Движки spintax
Один синтаксис spintax, четыре отдельных движка — для JavaScript, PHP, Python и Object Pascal. Каждый — самостоятельная реализация, а не порт остальных, и все четыре сверены по одному набору эталонных фикстур, так что шаблон рендерится одинаково, где бы вы его ни запустили. Лицензия MIT, ноль зависимостей, ни одного внешнего сервиса в цепочке.
Один синтаксис, четыре рантайма
Синтаксис spintax, описанный на этом сайте, — надмножество плоского {a|b|c}, на котором большинство инструментов останавливается. Он добавляет перестановки, переменные с областью видимости, условия по значению, согласование с числом с учётом локали, include и стадию пост-обработки. Это надмножество не привязано к одному языку: его реализуют четыре движка, а вы выбираете по тому рантайму, который у вас уже есть.
| Рантайм | Пакет | Установка | Лицензия |
|---|---|---|---|
| JavaScript / TypeScript | @spintax/core | npm install @spintax/core | MIT |
| PHP 8.0+ | spintax/core | composer require spintax/core | MIT |
| Python 3.10+ | spintax-core | pip install spintax-core | MIT |
| Object Pascal / Free Pascal | репозиторий spintax-win | git clone (без пакетного реестра) | MIT |
Все четыре — без зависимостей. WordPress-плагин под GPL-2.0 встраивает PHP-движок и добавляет сверху редактор, кэширование и привязки к полям; пакеты выше — это движок сам по себе, без предположений о том, где лежат ваши шаблоны.
Не порты — независимые реализации, сверенные по одному корпусу
«Один синтаксис на четырёх языках» легко заявить и трудно удержать. Два написанных вручную парсера расходятся в тот же миг, когда один чинит краевой случай, которого другой не видел. Эту семью держит вместе не общий код — его почти нет, — а общий эталонный корпус: набор языконезависимых фикстур, каждая из которых — входной шаблон плюс точный вывод, диагностика или извлечение, которые он обязан выдать.
Тест-сьют каждого движка загружает тот же корпус и проверяется по нему. Случай, который проходит в TypeScript и падает в Pascal, — это баг в Pascal, пойманный до релиза, а не различие, которое пользователи находят в проде. Корпус — это машинно проверяемый паритет, а не обещание в README.
- Корпус живёт вместе с эталонным движком. Новые фикстуры пишутся в
@spintax/core; сьюты PHP, Python и Pascal читают ровно тот же JSON. - Семантика зафиксирована, случайность — нет. Фикстуры фиксируют, что конструкция значит: какие варианты допустимы, как согласуется число, что выбирает условие. Они сознательно не фиксируют случайный выбор: вывод по seed воспроизводим внутри одного движка, а одинаковые случайные последовательности между движками — это не цель. Такие случаи приватны для движка и пропускаются намеренно.
Практическая выгода: можно написать шаблон в песочнице на этом сайте — она крутит JavaScript-движок — и быть уверенным, что PHP-джоба, Python-скрипт или Pascal-бинарник, который рендерит его в проде, прочитает его так же.
Выбор по рантайму
JavaScript и TypeScript — @spintax/core
Эталонный движок и дом корпуса. Ноль рантайм-зависимостей, ESM-first с двойным CJS, работает без изменений в Node 18+, на Cloudflare Workers и в браузере — именно он крутит песочницу на этом сайте.
import { render, validate, extract } from '@spintax/core';
render('{Hello|Hi} %name%!', { context: { name: 'Ada' }, seed: 42 });
// детерминирован при заданном seed; пост-обработка включена по умолчанию
Один вызов render() прогоняет весь пайплайн. У него самый богатый инструментарий из четырёх — переиспользуемый AST из parse, плюс analyze и neutralize, — а его диагностики несут стабильный, сверенный по корпусу code, что и позволяет песочнице сопоставлять ошибку с переведённым сообщением. Полный гайд по JavaScript разбирает API подробно.
PHP — spintax/core
Не привязан к фреймворку, PHP 8.0+, ext-mbstring — и больше ничего. В отличие от трёх других, этот пакет — не независимая реализация: это вынос собственного движка WordPress-плагина тем же правообладателем, перелицензированный под MIT, чтобы его мог использовать любой PHP-проект, а плагин остался под GPL.
use Spintax\Core\Pipeline;
$pipeline = new Pipeline();
echo $pipeline->render('{Hello|Hi} %name%!', ['name' => 'Ada']);
Аргумента seed нет — детерминизм вы внедряете, сконструировав пайплайн со своим селектором, — и стабильного code у диагностик тоже нет; валидатор отдаёт сообщение со строкой и колонкой, а хост ветвится по структуре. Полный гайд по PHP разбирает, что пакет несёт и чего сознательно не несёт.
Python — spintax-core
Python 3.10+, независимая реализация, сверенная по корпусу. API в snake_case и читается так, как ждёт Python: render(template, *, context=, seed=, post_process=).
from spintax_core import render, validate, parse
render("{Hello|Hi} there!", seed=42) # тот же seed — тот же вывод
render("Hi %name%!", context={"name": "Sam"}) # "Hi Sam!"
Исходники и полные заметки о паритете: репозиторий spintax-py.
Object Pascal / Free Pascal
Четвёртый движок и самый новый: реализация без зависимостей на Object Pascal, собранная Free Pascal 3.2.2+ в режиме {$mode delphi}. Пакетного реестра у него нет — вы клонируете репозиторий и подключаете модуль к своей программе.
uses Spintax;
var ctx: TSpContext;
begin
DefaultSystemCodePage := CP_UTF8; { объявить UTF-8 один раз }
ctx := Default(TSpContext);
ctx.PostProcess := True;
SpRender('{Hello|Hi} there!', ctx);
end;
Детерминизм здесь — это шов, а не seed: оставьте ctx.Rng равным nil для недетерминированного вывода или внедрите одну из поставляемых стратегий — TFirstRng, TLastRng, TSequenceRng или seeded-TMulberry32Rng. Движок реализует всё надмножество и проходит 168 из 172 случаев корпуса; четыре пропущенных — это приватные для движка проверки RNG. Тот же исходник компилируется без изменений и UTF-16-компилятором Object Pascal; эта портируемость поддерживается «попутно»: побочный эффект, а не вторая платформа.
Что общего и где расходятся
Каждый движок рендерит один и тот же синтаксис и возвращает валидный выбор для любого корректного шаблона. Расходятся они в эргономике — как вы дотягиваетесь до детерминизма, сколько инструментов идёт вокруг ядра и в каком виде возвращаются диагностики.
| JavaScript | PHP | Python | Pascal | |
|---|---|---|---|---|
| Воспроизводимый вывод | seed | внедрённый селектор | seed | внедрённый TSpRng |
| Пост-обработка по умолчанию | вкл | выкл (опц. аргумент) | вкл | выкл (обнулённая запись) |
| Переиспользуемый AST | да | да | да | да |
| Стабильный код диагностики | да | нет | нет | нет |
| Подробный гайд на сайте | да | да | репозиторий | репозиторий |
Что сверено между всеми четырьмя — это вердикт: валиден ли шаблон и во что рендерится конструкция, — а не формулировка объяснения проблемы и не конкретный случайный выбор.
Все под MIT — поэтому они сочетаются
Четыре пакета лицензированы под MIT и не зависят ни от чего. WordPress-плагин остаётся под GPL-2.0, а MIT совместим с GPL, так что GPL-плагин может использовать MIT-движок на PHP, тогда как обратное не сработало бы. Всё рендерится локально: ни внешнего сервиса, ни вызова на каждую генерацию, ни данных, покидающих ваш рантайм.