lexpr
Rust library for parsing, representing, and printing Lisp S-expressions.
Repository Health
Technical Analysis
lexpr is a Rust library for working with S-expressions — the parenthesized data notation of the Lisp family. It provides the lexpr::Value type for representing S-expression data in memory, a sexp! macro for embedding literal S-expressions directly in Rust source, and a configurable parser and printer that support several Lisp dialects (Scheme, Emacs Lisp, Common Lisp).
A companion crate, serde-lexpr, integrates lexpr with Serde so arbitrary Rust data structures can be serialized to and deserialized from S-expressions, much like serde_json does for JSON.
What You Get
- A
lexpr::Valuetype representing the full range of S-expression data, including cons cells and lists - A configurable parser and printer with per-dialect syntax options (Scheme, Emacs Lisp, Common Lisp)
- The
sexp!macro for embedding S-expression literals directly in Rust code - Optional Serde integration via the
serde-lexprcrate for (de)serializing Rust types
Common Use Cases
- Reading and writing configuration or data files in an S-expression format
- Interoperating with Lisp/Scheme tooling that exchanges data as S-expressions
- Serializing Rust data structures to a human-readable Lisp-style format via Serde
Under The Hood
Architecture - lexpr-rs is a Cargo workspace of three crates: lexpr (the public library), serde-lexpr (Serde bindings), and lexpr-macros (an internal proc-macro crate implementing sexp!). Inside the lexpr crate, the core Value type is built from submodules for number, cons, datum, and value, with dedicated parse/ and print/ module trees plus a syntax module that captures dialect-specific reader/printer options. The parser is driven by these syntax settings so the same engine reads multiple Lisp flavors.
Tech Stack - Pure Rust, edition 2021, MSRV 1.56. Runtime dependencies are lean — itoa and ryu for fast integer and float formatting — with the sexp! macro pulled in optionally via lexpr-macros. Testing uses criterion for benchmarks and quickcheck/rand for property-based tests.
Code Quality - The codebase is substantial (~340 KB of Rust) and thoroughly tested, combining unit tests, a tests.rs suite, and QuickCheck property tests, with CI and Codecov coverage reporting. Much of the parsing/printing structure is adapted from the well-regarded serde_json and serde_yaml projects, lending it a proven design.
API Design - The API mirrors serde_json closely: a central Value enum, a literal macro (sexp! vs json!), and separate parser/printer entry points, so the learning curve is gentle for Serde-ecosystem users. Dialect configuration is explicit through the syntax options, making the multi-Lisp support discoverable rather than magical.