SExpressions 0.1.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package SExpressions --version 0.1.0
                    
NuGet\Install-Package SExpressions -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="SExpressions" Version="0.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="SExpressions" Version="0.1.0" />
                    
Directory.Packages.props
<PackageReference Include="SExpressions" />
                    
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 SExpressions --version 0.1.0
                    
#r "nuget: SExpressions, 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 SExpressions@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=SExpressions&version=0.1.0
                    
Install as a Cake Addin
#tool nuget:?package=SExpressions&version=0.1.0
                    
Install as a Cake Tool

SExpressions

A small, dependency-free S-expression parser and writer for .NET.

It reads and writes the Lisp-style parenthesised syntax used by KiCad files, Guile/Scheme configuration and similar formats, into a plain object tree you can walk, query and serialise back out — and it round trips byte for byte. Quoting style, comments, source order and the original whitespace all survive a parse/write cycle, so you can load a 145 KB schematic, change one value, and write a diff that touches exactly that one value.

dotnet add package SExpressions

Quick start

using SExpressions;

// Parse every top-level form in a file. Parse() returns just the first.
var doc = SDocument.Load("board.kicad_pcb");

// Query by path or by a recursive walk.
var version = doc.Find("kicad_pcb/version")?.GetValueAsInt();
var nets    = doc.Forms.SelectMany(f => f.Descendants("net")).Count();

// Edit, then write back. Untouched forms keep their original bytes.
doc.Find("kicad_pcb/general/thickness")!.SetValue(0, "1.6");
doc.Save("board.kicad_pcb");

Writing is format-preserving by default. Ask for SExpressionFormat.Canonical when you want the whole document reformatted from the tree instead:

var text = doc.ToText(new SExpressionWriterOptions { Format = SExpressionFormat.Canonical });

Why "SExpressions" and not "SExpressionSharp"

This library was extracted from danielmeza/kicad-ultra, where it was called SExpressionSharp. It was renamed on the way out, before the first push to nuget.org, because a package ID can never be changed afterwards.

The Sharp suffix earns its keep when a library wraps something that already has a name and needs to say "this is the .NET one" — which is why the sibling package KiCadSharp kept its name: it reads as a third-party .NET client for KiCad, where a bare KiCad.* prefix would look like it came from the KiCad project and would be borrowing their trademark.

S-expressions are not anybody's product. There is nothing for the suffix to disambiguate here, so Sharp added noise and nothing else. The generic half of the split gets the generic name.

This is recorded here so the question is not re-litigated later: the two halves are named differently on purpose.

Fidelity, and how it is verified

The round-trip guarantee is not a claim, it is a gate. The suite parses and rewrites a corpus of 52 real KiCad 10.0.6 files — 1,968,451 bytes (schematics, boards, a symbol library, a worksheet and design rules) and asserts the output is byte-identical to the input. On top of that:

  • boards and schematics are re-opened with kicad-cli after a canonical round trip, and their netlists and DRC results compared;
  • tokens that other parsers silently drop (exclude_from_sim, do_not_autoplace, duplicate_pin_numbers_are_jumpers, embedded fonts, generator_version) are asserted to survive.

The corpus lives outside this repository. Point ORBION_KICAD_ROOT at a checkout of it to run those tests; without it they skip themselves and the parser/writer unit tests still run, which is what CI does.

dotnet test SExpressions.slnx -c Release

Benchmarks

dotnet run -c Release --project benchmarks/SExpressions.Benchmarks

A BenchmarkDotNet project with MemoryDiagnoser enabled, measuring parse, canonical write, format-preserving write, round trip and two query shapes against a large (142 KB) and a small (12 KB) real schematic. It requires the corpus and fails without it — a benchmark over substitute input produces numbers that look valid and mean nothing.

There is no separate Tokenize benchmark because there is no separate tokenizer to measure: SExpressionParser scans and builds in one pass. Parse (lean) stands in for the scanning floor — the same parse with source tracking and string pooling switched off.

Earlier versions of this code quoted a "3.1x faster round trip" figure. That number came from a hand-rolled best-of-five harness comparing this parser against the implementation it replaced, inside one process. That predecessor has been deleted, so the comparison no longer exists and has deliberately not been reconstructed. The BenchmarkDotNet numbers are not comparable to it — different methodology, different question.

History

git log here goes back to the original commits in kicad-ultra, including the acceptance suite that was committed red before the rewrite that made it pass. The history was extracted with git filter-repo, not copied.

License

MIT — see LICENSE.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on SExpressions:

Package Downloads
KiCadSharp

A .NET client for KiCad. Talks to a running KiCad over its nng IPC API to read and edit boards and project settings, and reads the on-disk s-expression symbol and footprint formats through SExpressions. The schematic side of KiCad's IPC API does not answer on KiCad 10; see the README.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.2.0 341 9/22/2026
0.1.3 573 9/10/2026
0.1.2 104 9/10/2026
0.1.1 555 9/7/2026
0.1.0 231 9/7/2026