An encoding, decoding & utility library for S-expressions in Rust
Rust
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
src
.gitignore
Cargo.toml
README.org

README.org

sexpr: A S-expression library for Rust

sexpr strives to be the canonical library for reading, manipulating and writing S-expressions in Rust.

The parser is fully featured, performant and can be configured to read almost any s-expression variant including ”Standard”, ”Advanced” and ”Canonical” formats.

Predefined compatibility configurations exist for a dozen standards and protocols including:

  • Standard
  • RFC 2693-compatible SPKI
  • SMTLIB & SMTLIBv2
  • GPG / libgcrypt
  • KiCad
  • R5RS-compatible abstract-syntax

Individual format options can be enabled or disabled, allowing you to parse virtually any variant.

Configuration

sexpr accepts numerous configuration

Predefined Configurations

Namesquare_bracketssemi_commentcolon_keywordshex_escapespipe_actionNotes
STANDARDBase64InteriorA generic ‘standard’ s-expression
SMTLIBQuoteInteriorA common interchange format for SAT and SMT solvers
KICADNoneA computer-aided design program
GUILENoneA scheme intended for embedding in existing C programs
CANONICALNoneA common, interchangable encoding for many cryptographic protocols.

Configuration Variables

semi_comment

Line comments can be enabled when parsing s-expressions by setting semi_comment = Some(&["#", ";"]).

This ignores the rest of the stream until encountering a newline or EOF, this does not comment out interior s-expressions like proposals like SRFI 62.

colon_keywords

Many Scheme implementations assign a special meaning to atoms beginning with #: or :, sexpr can parse these as ‘keywords’ or they can be treated as valid starting characters to an ordinary symbol. (item :keyword value :keyword2 value)

You can control this behaviour with ParseConfig.allow_keywords = Some(&["#", "#:"])

square_brackets

Some Lisp implementations and s-expressions allow square brackets ([ and ]) to be used as an alternative bracket character.

These brackets must still themselves remain matched.

radix_escape

#

Libgcrypt, GPG and numerous Scheme implementations allow you to enclose a hexadecimal string in # characters: (q #61626364656667#)

You can enable this with hex_escapes

#b and #x

In a similar fashion, #b and #x are used to specify binary and hexadecimal encodings of the number that follows the trailing letter.

((two-hundred-fifty-five #xff) would be encoded as List(Symbol(two-hundred-fifty-five), U64(255)) Similarly, (sixteen #b10000)) would be encoded as List(Symbol(sixteen), U64(16))

You can control if both of these are accepted with the radix_escape option.

Both

When both of these options are enabled in tandem, sexpr will use the following character to determine the variety of radix specification.

Neither

If radix_escape is false, the initial # character will be treated as an atom.

parse_pipe_behavior

Standard decoding treats the | character as a valid starting literal to any Atom, although two other options are permitted:

Advanced-style

Rivest-style ‘advanced’ encodings dictate a string between two | characters be decoded as a stream of u8 (octets) in Base64.

Use ParseConfig.pipe_action = ParsePipeBehavior::Base64Interior

SMTLIBv2

SMT and SAT solvers using this format use the | character to quote it’s interior, preserving line breaks and other whitespace in a Symbol.

Use ParseConfig.pipe_action = ParsePipeBehavior::QuoteInterior

transport

Today, sexpr supports the most common form of S-expression transport encoding, RFC 4648 Base64. To indicate that you’d like to encode or decode an S-expression as Base64, you can modify your configuration as following.

let mut config = STANDARD.copy()
mut.transport = TransportEncoding::Base64

If you’d like to add a new transport field, simple add to the TransportEncoding enum, and create a new trait that implements SexpTransport, the rest is handled for you.

Encoding

Tuple

Input
sexpr::encode(&(1, 2, 3));
Result
(1 2 3)

Tuple Struct

Input
#[derive(RustcEncodable, Debug)]
struct TupleStruct(i32, i32, i32);
let ts = TupleStruct(1, 2, 3);
sexpr::encode(&ts);
Result
((_field0 1) (_field1 2) (_field2 3))

Hash Tables

Input
let ht = HashMap::new();
ht.insert('a', 1);
ht.insert('b', 2);
ht.insert('c', 3);
sexpr::encode(&ht);
Result
((a . 1) (b . 2) (c . 3))

Tuple Variants

Input
struct Kangaroo(u32, String);
sexpr::encode(&Kangaroo(34, &"William");
Result
((variant kangaroo) 
 (fields (34 "William)))