# S-Expression

Key: `container.object.s_expression`

Balanced nested parenthetical serialization — the S-expression family: Lisp/Scheme source, linguistic constituency/dependency parse trees (Penn Treebank, OntoNotes: "(ROOT (S (NP (NN cat)) (VP (VBZ sits))))"), and code ASTs ("(program (call (id print) (string hi)))"). A nested structure of the form (head child child ...), distinguished from a comma/whitespace list by its balanced, recursively-nested parentheses.

Page: <https://meridian.online/docs/projects/finetype/types/container/object/s_expression>

Samples: `(ROOT (S (NP (NN cat)) (VP (VBZ sits))))`, `(program (call (id print) (string hello)))`, `(+ (* 2 3) (- 4 1))`, `(a (b c) (d (e f)))`, `(S (INTJ (UH Uh)) (, ,) (NP-SBJ (PRP I)) (VP (VBP think)))`

## Background

### Origin

In most modern S-expression notations, (x y z) stands for (x . (y . (z . NIL))).[[1]](https://en.wikipedia.org/wiki/S-expression) Whitespace-separated list notation is typical in Lisp.[[1]](https://en.wikipedia.org/wiki/S-expression)

RFC 9804, Simple Public Key Infrastructure (SPKI) S-Expressions, was written by R. Rivest and D. Eastlake 3rd and published in June 2025.[[2]](https://www.rfc-editor.org/rfc/rfc9804.txt) RFC 9804 says its S-expressions were originally developed in 1996 for SDSI, the Simple Distributed Security Infrastructure by Lampson and Rivest.[[2]](https://www.rfc-editor.org/rfc/rfc9804.txt) RFC 9804 says the origins of these S-expressions date back to McCarthy's LISP programming language.[[2]](https://www.rfc-editor.org/rfc/rfc9804.txt) According to RFC 9804, a specification was made publicly available as draft-rivest-sexp-00.txt on 4 May 1997 but was never actually submitted to the IETF.[[2]](https://www.rfc-editor.org/rfc/rfc9804.txt) RFC 9804 defines an S-expression informally as either an octet-string or a finite list of simpler S-expressions.[[2]](https://www.rfc-editor.org/rfc/rfc9804.txt) One of RFC 9804's design goals is that it should be easy to produce a unique canonical form of an S-expression for digital signature purposes.[[2]](https://www.rfc-editor.org/rfc/rfc9804.txt) RFC 9804's canonical form represents each octet-string in verbatim mode and puts no blanks between list elements or the surrounding parentheses.[[2]](https://www.rfc-editor.org/rfc/rfc9804.txt)

FineType defines container.object.s\_expression as a general type for balanced, nested parenthetical serialisation, covering Lisp and Scheme source, linguistic parse trees and code ASTs.[[3]](https://github.com/meridian-online/finetype/blob/main/labels/definitions_container.yaml) FineType's definition distinguishes an S-expression from a comma or whitespace list by its balanced, recursively nested parentheses.[[3]](https://github.com/meridian-online/finetype/blob/main/labels/definitions_container.yaml)

### Use

In the Lisp family of programming languages, S-expressions are used to represent both source code and data.[[1]](https://en.wikipedia.org/wiki/S-expression) When Lisp source code is written as an S-expression, the first element is commonly an operator or function name and the remaining elements are treated as arguments.[[1]](https://en.wikipedia.org/wiki/S-expression) Lisp programs are valid S-expressions, but not all S-expressions are valid Lisp programs.[[1]](https://en.wikipedia.org/wiki/S-expression) S-expressions are used for the WebAssembly text format.[[1]](https://en.wikipedia.org/wiki/S-expression) Wikipedia names IMAP among the communication protocols that use S-expressions as mark-up.[[1]](https://en.wikipedia.org/wiki/S-expression) RFC 9804 says its S-expressions are in active use between GnuPG and Ribose's RNP.[[2]](https://www.rfc-editor.org/rfc/rfc9804.txt) Libgcrypt's public key functions use S-expressions to pass complex data structures around.[[4]](https://www.gnupg.org/documentation/manuals/gcrypt/S_002dexpressions.html)

Treebank-3 is listed in the Linguistic Data Consortium catalogue as LDC99T42.[[5]](https://catalog.ldc.upenn.edu/LDC99T42) The Linguistic Data Consortium catalogue lists Mitchell P. Marcus, Beatrice Santorini, Mary Ann Marcinkiewicz and Ann Taylor as the authors of Treebank-3.[[5]](https://catalog.ldc.upenn.edu/LDC99T42) NLTK's Tree.fromstring reads a bracketed tree string, such as (S (NP (NNP John)) (VP (V runs))), and returns a tree.[[6]](https://www.nltk.org/api/nltk.tree.tree.html) NLTK's Tree.fromstring has a remove\_empty\_top\_bracketing option, which the NLTK documentation says is useful for treebank trees that sometimes contain an extra level of bracketing.[[6]](https://www.nltk.org/api/nltk.tree.tree.html) In the part-of-speech tags of the CoNLL-2012 OntoNotes v5 dataset on Hugging Face, -LRB- and -RRB- stand for the ( and ) characters.[[7]](https://huggingface.co/datasets/ontonotes/conll2012_ontonotesv5) Before you rebuild sentence text from a parse tree, map bracket tokens such as -LRB- and -RRB- back to the parentheses they stand for. (Meridian's view)

Tree-sitter query patterns are S-expressions that match nodes in a syntax tree.[[8]](https://tree-sitter.github.io/tree-sitter/using-parsers/queries/1-syntax.html) In a tree-sitter pattern, a node is matched by a pair of parentheses containing the node's type and, optionally, patterns for its children.[[8]](https://tree-sitter.github.io/tree-sitter/using-parsers/queries/1-syntax.html) Tree-sitter represents text its parser does not recognise as an (ERROR) node in the syntax tree.[[8]](https://tree-sitter.github.io/tree-sitter/using-parsers/queries/1-syntax.html) The tree-sitter parse command's --no-ranges option omits node ranges from the default parse output, which the documentation says is useful when copying S-expressions to a test file.[[9]](https://tree-sitter.github.io/tree-sitter/cli/parse.html) Before treating a column of syntax trees as clean, look for ERROR nodes and decide whether those rows belong in the analysis. (Meridian's view)

In Lisp-like languages a quoted string can typically contain anything but a quote.[[1]](https://en.wikipedia.org/wiki/S-expression) Line breaks usually qualify as separators in S-expression notation.[[1]](https://en.wikipedia.org/wiki/S-expression) Parse an S-expression with a reader built for its dialect, such as a Lisp reader or a treebank tree reader, rather than splitting it on commas or spaces. (Meridian's view)

### Where you'll meet it

The CoNLL-2012 OntoNotes v5 dataset on Hugging Face has a parse\_tree field stored as a string.[[7]](https://huggingface.co/datasets/ontonotes/conll2012_ontonotesv5) The Hugging Face card for the CoNLL-2012 OntoNotes v5 dataset describes the parse\_tree field as a serialized NLTK Tree that includes POS tags as pre-terminal nodes.[[7]](https://huggingface.co/datasets/ontonotes/conll2012_ontonotesv5) An example parse\_tree on the CoNLL-2012 OntoNotes v5 dataset card begins (TOP(S(NP (NNP CONCORDE).[[7]](https://huggingface.co/datasets/ontonotes/conll2012_ontonotesv5)

## Sources

1. [S-expression - Wikipedia](https://en.wikipedia.org/wiki/S-expression) — secondary source
2. [Simple Public Key Infrastructure (SPKI) S-Expressions](https://www.rfc-editor.org/rfc/rfc9804.txt) — standard
3. [finetype/labels/definitions\_container.yaml at main · meridian-online/finetype](https://github.com/meridian-online/finetype/blob/main/labels/definitions_container.yaml) — FineType
4. [S-expressions (The Libgcrypt Reference Manual)](https://www.gnupg.org/documentation/manuals/gcrypt/S_002dexpressions.html) — documentation
5. [Treebank-3 - Linguistic Data Consortium](https://catalog.ldc.upenn.edu/LDC99T42) — dataset
6. [NLTK :: nltk.tree.tree module](https://www.nltk.org/api/nltk.tree.tree.html) — documentation
7. [ontonotes/conll2012\_ontonotesv5 · Datasets at Hugging Face](https://huggingface.co/datasets/ontonotes/conll2012_ontonotesv5) — dataset
8. [Basic Syntax - Tree-sitter](https://tree-sitter.github.io/tree-sitter/using-parsers/queries/1-syntax.html) — documentation
9. [Parse - Tree-sitter](https://tree-sitter.github.io/tree-sitter/cli/parse.html) — documentation
