Spintax engines

One spintax syntax, four separate engines — for JavaScript, PHP, Python and Object Pascal. Each is an independent implementation, not a port of the others, and all four are held to the same corpus of golden fixtures so a template renders the same wherever you run it. MIT-licensed, zero dependencies, no external service in the loop.

One syntax, four runtimes

The spintax syntax documented on this site is a superset of the flat {a|b|c} that most tools stop at. It adds permutations, scoped variables, value-driven conditionals, locale-aware plural agreement, includes and a post-processing pass. That superset is not tied to one language: four engines implement it, and you pick by the runtime you already have.

RuntimePackageInstallLicense
JavaScript / TypeScript@spintax/corenpm install @spintax/coreMIT
PHP 8.0+spintax/corecomposer require spintax/coreMIT
Python 3.10+spintax-corepip install spintax-coreMIT
Object Pascal / Free Pascalthe spintax-win repositorygit clone (no package registry)MIT

All four are zero-dependency. The GPL-2.0 WordPress plugin embeds the PHP engine and adds an editor, caching and field bindings on top; the packages above are the engine on its own, with no assumptions about where your templates live.

Not ports — independent implementations held to one corpus

"The same syntax in four languages" is easy to claim and hard to keep true. Two hand-written parsers drift the moment one fixes an edge case the other has not seen. What holds this family together is not shared code — there is almost none — but a shared golden corpus: a set of language-neutral fixtures, each an input template plus the exact output, diagnostics or extraction it must produce.

Every engine's test suite loads that same corpus and asserts against it. A case that passes in TypeScript and fails in Pascal is a bug in Pascal, caught before release, not a difference users discover in production. The corpus is machine-checked parity, not a promise in a README.

  • The corpus lives with the reference engine. @spintax/core is where new fixtures are written; the PHP, Python and Pascal suites consume the exact same JSON.
  • Semantics are gated, randomness is not. The fixtures pin what a construct means — which options are valid, how a plural agrees, what a conditional selects. They deliberately do not pin the random pick: seeded output is reproducible within one engine, and identical random sequences across engines is a non-goal. Those cases are engine-private and skipped on purpose.

The practical payoff: you can author a template against the playground on this site — which runs the JavaScript engine — and trust that the PHP job, the Python script or the Pascal binary that renders it in production will read it the same way.

Pick by runtime

JavaScript & TypeScript — @spintax/core

The reference engine, and the home of the corpus. Zero runtime dependencies, ESM-first with dual CJS, and it runs unchanged on Node 18+, Cloudflare Workers and in the browser — it is what powers the playground on this site.

import { render, validate, extract } from '@spintax/core';

render('{Hello|Hi} %name%!', { context: { name: 'Ada' }, seed: 42 });
// deterministic for a given seed; post-processed by default

One render() call runs the whole pipeline. It carries the richest tooling of the four — a reusable AST from parse, plus analyze and neutralize — and its diagnostics carry a stable, parity-gated code, which is what lets the playground map an error to a translated message. The full JavaScript guide covers the API surface in depth.

PHP — spintax/core

Framework-agnostic, PHP 8.0+, ext-mbstring, nothing else. Unlike the other three, this package is not an independent reimplementation: it is an extraction of the WordPress plugin's own engine by the same copyright holder, relicensed MIT so any PHP app can use it while the plugin stays GPL.

use Spintax\Core\Pipeline;

$pipeline = new Pipeline();
echo $pipeline->render('{Hello|Hi} %name%!', ['name' => 'Ada']);

There is no seed argument — you inject determinism by constructing the pipeline with your own selector — and no stable diagnostic code; the validator reports a message with a line and column, and a host branches on structure. The full PHP guide covers what the package does and deliberately does not carry.

Python — spintax-core

Python 3.10+, an independent implementation held to the corpus. The API is snake_case and reads the way Python expects: render(template, *, context=, seed=, post_process=).

from spintax_core import render, validate, parse

render("{Hello|Hi} there!", seed=42)             # same seed, same output
render("Hi %name%!", context={"name": "Sam"})    # "Hi Sam!"

Source and full parity notes: the spintax-py repository.

Object Pascal / Free Pascal

The fourth engine, and the newest: a zero-dependency implementation in Object Pascal, built with Free Pascal 3.2.2+ in {$mode delphi}. It has no package registry — you clone the repository and add the unit to your program.

uses Spintax;

var ctx: TSpContext;
begin
  DefaultSystemCodePage := CP_UTF8;   { declare UTF-8 once }
  ctx := Default(TSpContext);
  ctx.PostProcess := True;
  SpRender('{Hello|Hi} there!', ctx);
end;

Determinism is a seam rather than a seed: leave ctx.Rng nil for non-deterministic output, or inject one of the shipped strategies — TFirstRng, TLastRng, TSequenceRng, or a seeded TMulberry32Rng. It implements the full superset and passes 168 of the corpus's 172 cases, the four skipped being the engine-private RNG assertions. The source also compiles unchanged under a UTF-16 Object Pascal compiler; that portability is kept, not maintained — a supported side effect, not a second platform.

What they share, where they differ

Every engine renders the same syntax and returns a valid pick for any well-formed template. Where they differ is ergonomics — how you reach for determinism, how much tooling ships around the core, and how diagnostics come back.

JavaScriptPHPPythonPascal
Reproducible outputseedinjected selectorseedinjected TSpRng
Post-processing defaultonoff (opt-in arg)onoff (zeroed record)
Reusable ASTyesyesyesyes
Stable diagnostic codeyesnonono
Deep guide on this siteyesyesreporepo

What is gated across all four is the verdict — whether a template is valid and what a construct renders to — not the wording used to explain a problem or the exact random pick.

All MIT, so they compose

The four packages are MIT-licensed and depend on nothing. The WordPress plugin stays GPL-2.0, and MIT is GPL-compatible, so the GPL plugin can consume the MIT PHP engine while the reverse would not hold. Everything renders locally: no external service, no per-render call, no data leaving your runtime.