How unight0/splib works
Purpose and Integration Model
SPLIB is a C library for lexing, parsing, printing, and serializing S-expressions. It is distributed as a single-header implementation: consumers define _SPLIB_IMPLEMENTATION in exactly one translation unit, then include the public API elsewhere. The library’s complete runtime and API reside in splib.h.
Primary consumer entry point: include splib.h, construct a lexer with source text, lex tokens, construct a parser, and parse an AST.
Library Core
splib.h contains the architecture-defining data model and implementation:
• Lexer state, token kinds, token linked-list construction, and lexical error reporting.
• Parser state and recursive parsing of expressions into AST nodes.
• AST representation for roots, lists, dotted pairs, quote forms, and atomic values.
• AST printing and recursive destruction.
• Growable serializer state and AST-to-S-expression serialization.
The library owns its allocated lexer, parser, tokens, AST nodes, and serializer buffers, but deliberately does not own the caller-provided source string.
Lexing Stage
The lexer transforms a NUL-terminated source buffer into a linked token stream via new_lexer and lex_all in splib.h. It recognizes names, strings, numeric bases, character strings, parentheses, dot, quote/backquote/comma forms, whitespace, and semicolon comments.
Lexing is a hard boundary: malformed lexical input produces stderr diagnostics and a failure return from lex_all; unmatched parentheses are surfaced through lexer balance state. The caller retains responsibility for token cleanup with destroy_all_tokens.
Parsing and AST Normalization
new_parser consumes the token-list head and parse_root builds an AK_ROOT AST containing sequential expressions, all in splib.h. Recursive parsing creates:
• Proper lists (AK_TREE)
• Dotted structures (AK_DOTTED)
• Quote, backquote, unquote, and splice-unquote nodes
• Atomic values
Dotted-list handling is also a normalization boundary: nested dotted cdrs are flattened, and dotted structures ending in a proper list become proper lists. Parser failure is represented by NULL returns plus parser->error, rather than process termination.
Output Paths
print_AST in splib.h provides a diagnostic tree rendering to stdout. serialize_AST reconstructs valid S-expression text into an SSerializer growable output buffer, created by new_sserializer. Serialization handles every AST form and exposes both the appended slice and final NUL-terminated ser->output.
The example driver is examples/sp.c; it is the executable-oriented entry point for parsing argv or stdin and printing the AST. The Makefile builds this as sp.
Build, Fuzzing, and Validation Boundary
The project is native C, using Make-based local builds; no service, database, network, or web runtime is present. Makefile defines normal example builds plus AFL++-instrumented fuzz targets.
Fuzzing exercises the public parse pipeline through seeded syntax cases in in-fuzz/, including malformed nesting, atom formats, quote forms, dotted pairs, comments, and strings. splib.dict supplies AFL++ token and form hints. AFL++ is the only material external tool/service dependency, used for robustness validation rather than runtime operation.
Main entry points: splib.h Lexer · Makefile AFL++ fuzzing · sp.c sp example driver · splib.dict AFL++ dictionary
Components 17
Consumer integration
- Source text · NUL-terminated buffer remains caller-owned.
- Consumer translation unit · Defines _SPLIB_IMPLEMENTATION in exactly one translation unit.
- sp example driver · Parses argv or stdin and prints the AST.
examples/sp.c
SPLIB core
Single-header public API and implementation.
- Public API + implementation · Complete SPLIB runtime resides in one header.
splib.h - Lexer · Recognizes S-expression syntax and tracks parenthesis balance.
splib.h - Token stream · Caller destroys allocated tokens after use.
splib.h - Lexical failure · Reports to stderr and makes lex_all fail.
splib.h - Recursive parser · Consumes token-list head; failures use NULL and parser->error.
splib.h - Normalized AST · Represents roots, lists, dotted pairs, quote forms, and atoms.
splib.h - Dotted-list normalization · Flattens nested cdrs and converts proper-list endings.
splib.h
Output paths
- AST printer · print_AST writes a tree rendering to stdout.
splib.h - AST serializer · serialize_AST reconstructs valid S-expression text.
splib.h - Serialized output · Exposes appended slice and NUL-terminated ser->output.
splib.h
Build and fuzzing
- Make build · Builds the example as sp and defines fuzz targets.
Makefile - AFL++ fuzzing · Instrumented targets exercise the public parse pipeline.
Makefile - Syntax seed corpus · Covers malformed nesting, atoms, quotes, dotted pairs, comments, and strings.
- AFL++ dictionary · Supplies token and form hints.
splib.dict
Connections 18
- Consumer translation unit → Public API + implementation: includes
- Source text → Lexer: new_lexer / lex_all
- Public API + implementation → Lexer: provides
- Lexer → Token stream: builds
- Lexer → Lexical failure: malformed input
- Token stream → Recursive parser: new_parser
- Recursive parser → Normalized AST: parse_root
- Dotted-list normalization → Normalized AST: canonicalizes
- Recursive parser → Dotted-list normalization: handles dotted forms
- Normalized AST → AST printer: print_AST
- Normalized AST → AST serializer: serialize_AST
- AST serializer → Serialized output: grows and terminates
- sp example driver → Public API + implementation: uses
- Make build → sp example driver: builds sp
- Make build → AFL++ fuzzing: builds instrumented targets
- Syntax seed corpus → AFL++ fuzzing: seed inputs
- AFL++ dictionary → AFL++ fuzzing: syntax hints
- AFL++ fuzzing → Lexer: fuzzes parse pipeline