Embedding in .NET
UniversalToolchain is intended to be embedded into .NET applications. The CLI is useful for learning and smoke checks, but production-style DSL usage normally runs through a host API.
When to read this page
Read this after Minimal DSL when you want to run formulas or restricted scripts from application code.
Goal
Create a Wist runtime facade, select a shipped dialect preset, pass source text and named inputs, choose a backend and read the result.
Minimal host shape
A host application should treat the DSL runtime as a composed execution surface:
select dialect or preset
-> create runtime facade
-> pass source and declared inputs
-> choose requested backend
-> receive result or explicit failureThe facade is an entry point into the same dialect/runtime composition model. It is not a second implementation of Wist. This page uses lower-level dialect-runtime APIs; first-contact application code should prefer the UniversalToolchain.Wist package facade unless it specifically needs custom dialect composition.
Example: restricted pricing expression
using UniversalToolchain.Dialects.Wist.Facade;
using var runtime = WistRuntimeFacadeBuilder
.CreateDefault()
.WithShippedDialectPreset("pricing-restricted")
.Build();
var result = runtime.Run(
"price + fee * 2",
new Dictionary<string, object?>
{
["price"] = 100,
["fee"] = 5
},
backend: "compiler");Expected numeric result:
110The important contract is that the host selects a dialect, supplies inputs explicitly and requests a backend intentionally.
Why inputs are explicit
Runtime inputs such as price and fee are host-provided bindings. They are not the same concept as local variables declared by the Wist program.
A correct runtime path must keep these concerns separate:
external binding: provided by the host
local variable: declared and mutated by the programThis boundary matters for formula DSLs because a host parameter should not accidentally overwrite an unrelated local variable.
Choosing a dialect for embedding
Use the narrowest dialect that supports the business case.
| Scenario | Recommended dialect shape |
|---|---|
| Pricing/scoring formula | arithmetic, identifiers, selected variable/input support, no interop |
| Workflow-like script | variables, conditions and loops only if needed |
| Demonstrating Wist | broad reference profile |
| Untrusted user-authored code | narrow dialect plus process/resource isolation outside Wist |
Do not embed full-default only because it is convenient. A broad profile exposes more syntax and runtime behavior than a restricted formula surface usually needs.
Backend selection
backend: "compiler" requests the CIL-backed compiler path when the selected dialect exposes CIL. backend: "interpreter" requests the interpreter path when exposed.
When both backends are available, add parity tests for important formulas. When only one backend is available, test that the unsupported backend request fails explicitly.
What to test in a host application
A host using Wist should test:
- valid formula execution;
- missing input behavior;
- wrong input type behavior;
- syntax rejected when the owning module is omitted;
- backend availability failures;
- compiler/interpreter parity when both modes are exposed;
- interop rejection for restricted formula dialects.
Security boundary
A restricted dialect is a composition constraint. It limits which modules, syntax and runtime components are selected. It is not a hardened process sandbox.
For arbitrary untrusted third-party code, combine dialect restriction with external isolation, resource limits and host-level policy.
Next
Continue with Backend Selection, Testing a DSL and Backend Contracts.