Spintax.Core 0.1.0

dotnet add package Spintax.Core --version 0.1.0
                    
NuGet\Install-Package Spintax.Core -Version 0.1.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Spintax.Core" Version="0.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Spintax.Core" Version="0.1.0" />
                    
Directory.Packages.props
<PackageReference Include="Spintax.Core" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Spintax.Core --version 0.1.0
                    
#r "nuget: Spintax.Core, 0.1.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Spintax.Core@0.1.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Spintax.Core&version=0.1.0
                    
Install as a Cake Addin
#tool nuget:?package=Spintax.Core&version=0.1.0
                    
Install as a Cake Tool

Spintax.Core (.NET)

NuGet CI License: MIT

A Spintax engine for .NET — parse, render, validate, extract, analyze and neutralize spintax templates. MIT, zero dependencies, netstandard2.0 and net472 from one source: it runs on .NET Framework 4.7.2+ and on every .NET since.

This is the fifth engine in the Spintax family, and an independent implementation — not a transcription of the others. It is held to the same behaviour contract by a shared golden corpus of language-neutral fixtures, the one that gates the TypeScript, PHP, Python and Object Pascal engines. All 258 of its cases pass here, on both targets, none skipped, none expected to fail — the 254 deterministic ones on output, the 4 kind:rng ones through the engine's RNG seam.

Install

dotnet add package Spintax.Core

Use

using System.Collections.Generic;
using Spintax.Core;

Engine.Render("{Hello|Hi} there!");                                   // "Hello there!" or "Hi there!"
Engine.Render("{Hello|Hi} there!", new RenderOptions { Seed = "42" }); // same seed, same output, every time
Engine.Render("Hi %name%!", new RenderOptions
{
    Context = new Dictionary<string, string> { ["name"] = "Sam" }      // "Hi Sam!"
});

// Check a template before you ship it: diagnostics carry a stable code and a 1-based position.
foreach (var d in Engine.Validate("{a|b"))
    System.Console.WriteLine($"{d.Severity} {d.Line}:{d.Column} [{d.Code}] {d.Message}");
// Error 1:1 [bracket.unclosed] Unclosed '{'.

// How much a template can say — counted by walking the tree, not sampled.
Engine.Combinations("{a|b} {c|d|e}");                                  // 6

Rendering is lenient: malformed markup degrades rather than throwing, so a template a non-programmer wrote cannot take a page down. Output is tidied up by default — sentence capitalisation, spacing around punctuation, URLs and abbreviations left intact; pass PostProcess = false to get the raw pick.

Values in Context are re-parsed as templates when they contain {, [ or % — the reference engine does the same, so a host can pass spintax through a variable on purpose. Data from a table or a scraped page must go through Engine.Neutralize(value) first: it shields the structural characters so that Aurix {Mini|Maxi} prints as written instead of spinning.

Syntax — enumerations {a|b}, permutations [<minsize=2;sep=", ">a|b|c], variables %name%, conditionals {?VAR?yes|no}, plural agreement {plural %n%: one|few|many}, comments /# … #/, and the #set / #def / #include directives — is documented in full at spintax.net/docs.

Public API

static class Engine
{
    string                    Render(string template, RenderOptions? options = null);
    IReadOnlyList<Diagnostic> Validate(string template, ValidateOptions? options = null);
    ExtractResult             Extract(string template);      // variables referenced and defined, includes
    Analysis                  Analyze(string template, ValidateOptions? options = null);
    string                    Neutralize(string value);
    long                      Combinations(string template, IReadOnlyDictionary<string,string>? vars = null, string? locale = null);
    long                      MaxLength(string template, IReadOnlyDictionary<string,string>? vars = null, string? locale = null);
}
class RenderOptions   { Context; Seed; Locale; IncludeResolver; PostProcess = true; MaxDepth; OnPluralError; }
class ValidateOptions { Locale; KnownIncludes; KnownVariables; }

Flat signatures on purpose — strings, dictionaries, small DTOs, no Task<T> — so the engine drops into hosts that load a dll by name and compile snippets against it — ZennoPoster was the first, and its facade, project templates and measurements ship with that plugin. No mutable static state: dozens of threads can render with the same seed and get the same bytes.

Combinations counts choice paths — the number of distinct texts when no two options spell the same thing ({a|a} is 2). With vars it is the count for that one row of data (a conditional takes its branch, a plural its form); without, across every state of the data.

The family

The engines are independent implementations held together by the shared corpus, not ports of one another's code. A template written for one renders the same on every other; a seed reproduces a draw within an engine, not across them (the deterministic fixtures use an injected RNG strategy, so the contract does not depend on it).

Why another one

The .NET packages named for spintax implement the flat {a|b} dialect of the 2010s. This engine implements the spintax.net superset — permutations that pick and order a subset, scoped variables, value-driven conditionals, locale-aware plural agreement for the three-form languages (ru, uk, be, sr, hr, bs) as well as the two-form default, includes, and a post-processing pass — with the diagnostics and the determinism a pipeline needs, and it is tested against the same corpus as four other implementations rather than against its own expectations.

Conformance

tests/Spintax.Corpus loads the shared golden corpus (the exact JSON fixtures the other engines consume) and asserts every case. tests/known-failures.txt is a baseline in the Pascal port's sense: a new failure fails the build, and a case that starts passing must be removed from the file in the same commit. It is empty.

PASS=258  FAIL=0  SKIP=0    # net8.0 and net472, 2026-08-22

The port's own unit tests (tests/Spintax.Core.Tests, 157 on each host) pin what the corpus cannot express: 1-based positions, plural buckets for locales the corpus lacks, JavaScript text semantics (JsText: the JS whitespace set, the four line terminators, String.prototype.toUpperCase special casing), the exact census, and the assembly contract — two targets, zero package references, AnyCPU, no mutable static state, no Task<T> on the surface.

Build and test

The full gate needs Windows with the .NET SDK 8 — the net472 test host and the net472 corpus run need the .NET Framework runtime, and the library deliberately adds no reference-assemblies package. On Linux or macOS build and test the net8.0 host only (dotnet test -f net8.0, dotnet run … -f net8.0); the netstandard2.0 library builds anywhere.

git clone https://github.com/investblog/spintax-js ../spintax-js     # the corpus, once
dotnet build Spintax.sln -nologo -v q                                # warnings are errors
dotnet test Spintax.sln --no-build -nologo -v q                      # net8.0 and net472
dotnet run --project tests/Spintax.Corpus -f net8.0 --no-build -- --baseline tests/known-failures.txt
dotnet run --project tests/Spintax.Corpus -f net472 --no-build -- --baseline tests/known-failures.txt

The corpus is read from SPINTAX_FIXTURES or the sibling checkout — never vendored (docs/decisions/0001). Without it the runner fails (exit 2) rather than passing an empty run. Why two targets: docs/decisions/0002 — a .NET Framework host that compiles snippets against the dll has no netstandard.dll, so the net472 build is the one that ships there.

License

MIT. The WordPress plugin remains GPL; MIT/Expat is GPL-compatible.


Part of the 301.st toolset. Product home: spintax.net.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 is compatible.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETFramework 4.7.2

    • No dependencies.
  • .NETStandard 2.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.0 41 8/22/2026