FScript is a lightweight, embeddable interpreter with an F#/ML-style language.
It is designed for host applications that need:
- a concise functional scripting language,
- strong static checks (Hindley-Milner type inference),
- controlled host extensibility,
- and a clear sandbox/security boundary.
- F#/ML lineage: immutable data, expressions-first style, pattern matching,
let/fun, algebraic modeling. - Interpreter pipeline: lexer -> parser -> type inference -> evaluator.
- Host-first extensibility: add external functions with explicit type schemes and runtime implementations.
- Security-aware embedding: script capabilities are defined by what the host exposes.
FScript also compiles to optimized .NET assemblies and independently buildable C# projects. The .NET backend supports self-contained single-file and Native AOT executables, including typed FFI bindings.
See Compilation for target selection, .NET output, C# projects, and optimization behavior.
FScript currently includes:
- bindings and functions:
let, zero-argument functions such aslet f () = ...,let rec,andmutual recursion, lambdas, - control flow:
if/elif/else,match,for ... in ... do, - data: list, option, tuple, map, record, discriminated unions,
- pattern matching: list, option, tuple, record, union cases,
- optional type annotations on parameters,
- type declarations: records and unions (including recursive forms),
- interpolation, pipeline operator,
typeoftype tokens, andnameofidentifier tokens for host workflows. - unified brace literals for records/maps (
{ Field = value },{ [key] = value },{}for empty map), with map keys typed asstring,int,bool, or a union whose cases all have no payload. Optional key annotations use postfix syntax such asstring map[int].
If you are new to FScript, start with the progressive tutorial:
It covers installation, syntax basics, flow control, collections, pattern matching, stdlib usage, includes, and host/export concepts.
dotnet tool install --global MagnusOpera.FScriptTo update:
dotnet tool update --global MagnusOpera.FScriptTo install in a local tool manifest instead:
dotnet new tool-manifest
dotnet tool install MagnusOpera.FScriptbrew install magnusopera/tap/fscriptMagnusOpera.FScript.LanguageMagnusOpera.FScript.RuntimeMagnusOpera.FScript.Extensibility(independent SDK for extension authors)
FScript has a first-party VS Code extension with syntax highlighting and Language Server features (diagnostics, completion, hover, symbols, go-to-definition, references, rename, semantic tokens, inlay hints).
- VS Code Marketplace:
https://marketplace.visualstudio.com/items?itemName=MagnusOpera.fscript - Open VSX:
https://open-vsx.org/extension/MagnusOpera/fscript
The extension uses automatic .NET runtime acquisition via .NET Install Tool and falls back to dotnet from PATH when needed.
Source lives in vscode-fscript/.
Building from source requires Make, the latest stable Terrabuild executable on PATH, and a running Docker engine. make build uses Terrabuild to prepare a container with the .NET SDK, Node.js, and native build tools. CI uses Terrabuild 0.207.2 or newer to restore cached test reports and render cached operation logs. Shared tool versions are defined in WORKSPACE.
See the Terrabuild guide for setup, caching, and container-based test commands such as make tb-test and make tb-check. The make test and dotnet run commands below use the host .NET SDK; make test also requires Node.js and npm on the host.
make buildmake testdotnet run --project src/FScript -- samples/types-showcase.fssRun a script with script arguments (after --):
dotnet run --project src/FScript -- samples/fibonacci.fss -- 10Run the vintage BASIC sample interpreter:
dotnet run --project src/FScript -- samples/basic/basic.fss -- helloworld.basOptional sandbox root override:
dotnet run --project src/FScript -- --root /tmp/sandbox samples/types-showcase.fssDisable default runtime externs (Registry.all):
dotnet run --project src/FScript -- --no-default-externs samples/types-showcase.fssShare a configured external-function environment with VS Code:
fscript scripts/build.fss
# Override automatic selection when the manifest is elsewhere:
fscript --config ./fscript.toml scripts/build.fssThe CLI loads fscript.toml beside the script automatically; explicit --config selects a different location. See environment configuration for the schema, flag precedence, workspace selection, and trust rules.
Load user extern providers from an assembly (repeatable):
dotnet run --project src/FScript -- --extern-assembly ./MyExterns.dll samples/types-showcase.fssRun a script from stdin:
cat samples/types-showcase.fss | dotnet run --project src/FScript -- -r .Run stdin mode with script arguments:
cat samples/types-showcase.fss | dotnet run --project src/FScript -- -r . -- foo barShow CLI version:
dotnet run --project src/FScript -- versionIn CLI execution modes, scripts get:
- stdlib
type Environment = { ScriptName: string option; Arguments: string list } - stdlib
type FsKind = File of string | Directory of string | Missing - CLI-injected
let Env = ...
Start REPL:
dotnet run --project src/FScript --The CLI automatically loads fscript.toml beside the entry script, or in the working directory for stdin/REPL. It does not search parent directories. Use --config to select another manifest or --no-config to skip discovery.
Useful samples:
samples/ffi: a rotating OpenGL teapot with FScript-generated geometry, lighting, and camera, typed host functions, and opaque GPU resources.samples/types-showcase.fsssamples/patterns-and-collections.fsssamples/quicksort.fsssamples/parallel-quicksort.fsssamples/tree.fsssamples/mutual-recursion.fsssamples/imports-and-exports.fsssamples/basic/basic.fsswith bundled.basprograms such ashelloworld.bas
The core engine lives in src/FScript.Language and runs in four stages:
- Lexing: indentation-aware tokenization.
- Parsing: AST construction with expression/layout rules.
- Type inference: Hindley-Milner inference + unification + optional annotations.
- Evaluation: typed AST evaluation with immutable values and pattern matching.
Host integration lives in src/FScript.Runtime.
FScript is extended through host-provided externs. Registrations can also expose host-managed types with immutable visible fields and opaque host state; scripts can read and transport them but cannot construct or update them. See the embedding guide for a complete example.
Each extern declares:
- a public name (for script calls),
- a type scheme,
- arity,
- implementation.
Built-in host extern families include Fs.*, Console.*, Json.*, Xml.*, Regex.*, hashing, and GUID helpers.
Task.*, List.*, Map.*, and Option.* are provided by the embedded stdlib prelude.
For details and extension workflow, see docs/specs/external-functions.md.
FScript runs in-process. Security is capability-based:
- scripts can only do what exposed externs allow,
- core language evaluation is deterministic over in-memory values,
- side effects and external I/O are controlled at host extern boundaries.
Operational controls (timeouts, cancellation, resource limits, process/container isolation) are host responsibilities.
See docs/specs/sandbox-and-security.md for the full model and checklist.
- Documentation website:
https://fscript.magnusopera.io/ - Documentation portal:
docs/README.md - Tutorial:
docs/guides/getting-started-tutorial.md - Complete standard-library signature index
- Syntax and operators
- Specifications index:
docs/specs/README.md - Architecture index:
docs/architecture/README.md - FScript vs F# / OCaml:
docs/guides/fsharp-ocaml-differences.md
Website development commands:
make website-install
make website-build
make website
make website-version version=X.Y.ZThis project is licensed under the MIT License.
See LICENSE.
