# unight0/splib architecture

> Architecture diagram of the GitHub repository unight0/splib, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-08-01). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/unight0/splib
- Repository: https://github.com/unight0/splib

## Overview

#### 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`](https://github.com/unight0/splib/blob/master/splib.h) (Lexer), [`Makefile`](https://github.com/unight0/splib/blob/master/Makefile) (AFL++ fuzzing), [`examples/sp.c`](https://github.com/unight0/splib/blob/master/examples/sp.c) (sp example driver), [`splib.dict`](https://github.com/unight0/splib/blob/master/splib.dict) (AFL++ dictionary)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_consumer["Consumer integration"]
  node_source["Source text<br/>caller input"]
  node_consumer_tu["Consumer translation unit<br/>C consumer"]
  node_example_cli["sp example driver<br/>CLI executable<br/>[sp.c]"]
end

subgraph group_core["SPLIB core"]
  node_public_api["Public API + implementation<br/>single-header C library<br/>[splib.h]"]
  node_lexer["Lexer<br/>lexing state<br/>[splib.h]"]
  node_tokens["Token stream<br/>linked tokens<br/>[splib.h]"]
  node_lex_error(("Lexical failure<br/>diagnostic boundary<br/>[splib.h]"))
  node_parser["Recursive parser<br/>parser state<br/>[splib.h]"]
  node_ast{{"Normalized AST<br/>AST nodes<br/>[splib.h]"}}
  node_dotted_normalization["Dotted-list normalization<br/>parser rule<br/>[splib.h]"]
end

subgraph group_outputs["Output paths"]
  node_ast_printer["AST printer<br/>diagnostic renderer<br/>[splib.h]"]
  node_serializer["AST serializer<br/>growable serializer<br/>[splib.h]"]
  node_serialized_output["Serialized output<br/>buffer + slice<br/>[splib.h]"]
end

subgraph group_validation["Build and fuzzing"]
  node_make["Make build<br/>native build"]
  node_afl{{"AFL++ fuzzing<br/>robustness validation"}}
  node_fuzz_seeds["Syntax seed corpus<br/>fuzz inputs"]
  node_fuzz_dictionary["AFL++ dictionary<br/>fuzz hints<br/>[splib.dict]"]
end

node_consumer_tu -->|"includes"| node_public_api
node_source -->|"new_lexer / lex_all"| node_lexer
node_public_api -->|"provides"| node_lexer
node_lexer -->|"builds"| node_tokens
node_lexer -->|"malformed input"| node_lex_error
node_tokens -->|"new_parser"| node_parser
node_parser -->|"parse_root"| node_ast
node_dotted_normalization -->|"canonicalizes"| node_ast
node_parser -->|"handles dotted forms"| node_dotted_normalization
node_ast -->|"print_AST"| node_ast_printer
node_ast -->|"serialize_AST"| node_serializer
node_serializer -->|"grows and terminates"| node_serialized_output
node_example_cli -->|"uses"| node_public_api
node_make -->|"builds sp"| node_example_cli
node_make -->|"builds instrumented targets"| node_afl
node_fuzz_seeds -->|"seed inputs"| node_afl
node_fuzz_dictionary -->|"syntax hints"| node_afl
node_afl -.->|"fuzzes parse pipeline"| node_lexer

click node_public_api "https://github.com/unight0/splib/blob/master/splib.h"
click node_lexer "https://github.com/unight0/splib/blob/master/splib.h"
click node_tokens "https://github.com/unight0/splib/blob/master/splib.h"
click node_lex_error "https://github.com/unight0/splib/blob/master/splib.h"
click node_parser "https://github.com/unight0/splib/blob/master/splib.h"
click node_ast "https://github.com/unight0/splib/blob/master/splib.h"
click node_dotted_normalization "https://github.com/unight0/splib/blob/master/splib.h"
click node_ast_printer "https://github.com/unight0/splib/blob/master/splib.h"
click node_serializer "https://github.com/unight0/splib/blob/master/splib.h"
click node_serialized_output "https://github.com/unight0/splib/blob/master/splib.h"
click node_example_cli "https://github.com/unight0/splib/blob/master/examples/sp.c"
click node_make "https://github.com/unight0/splib/blob/master/Makefile"
click node_afl "https://github.com/unight0/splib/blob/master/Makefile"
click node_fuzz_dictionary "https://github.com/unight0/splib/blob/master/splib.dict"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_source,node_consumer_tu,node_example_cli toneBlue
class node_public_api,node_lexer,node_tokens,node_lex_error,node_parser,node_ast,node_dotted_normalization toneAmber
class node_ast_printer,node_serializer,node_serialized_output toneMint
class node_make,node_afl,node_fuzz_seeds,node_fuzz_dictionary toneRose
```

## Components

### Consumer integration

- **Source text**: caller input · NUL-terminated buffer remains caller-owned.
- **Consumer translation unit**: C consumer · Defines _SPLIB_IMPLEMENTATION in exactly one translation unit.
- [sp example driver](https://github.com/unight0/splib/blob/master/examples/sp.c): CLI executable · Parses argv or stdin and prints the AST. · `examples/sp.c`

### SPLIB core

Single-header public API and implementation.

- [Public API + implementation](https://github.com/unight0/splib/blob/master/splib.h): single-header C library · Complete SPLIB runtime resides in one header. · `splib.h`
- [Lexer](https://github.com/unight0/splib/blob/master/splib.h): lexing state · Recognizes S-expression syntax and tracks parenthesis balance. · `splib.h`
- [Token stream](https://github.com/unight0/splib/blob/master/splib.h): linked tokens · Caller destroys allocated tokens after use. · `splib.h`
- [Lexical failure](https://github.com/unight0/splib/blob/master/splib.h): diagnostic boundary · Reports to stderr and makes lex_all fail. · `splib.h`
- [Recursive parser](https://github.com/unight0/splib/blob/master/splib.h): parser state · Consumes token-list head; failures use NULL and parser->error. · `splib.h`
- [Normalized AST](https://github.com/unight0/splib/blob/master/splib.h): AST nodes · Represents roots, lists, dotted pairs, quote forms, and atoms. · `splib.h`
- [Dotted-list normalization](https://github.com/unight0/splib/blob/master/splib.h): parser rule · Flattens nested cdrs and converts proper-list endings. · `splib.h`

### Output paths

- [AST printer](https://github.com/unight0/splib/blob/master/splib.h): diagnostic renderer · print_AST writes a tree rendering to stdout. · `splib.h`
- [AST serializer](https://github.com/unight0/splib/blob/master/splib.h): growable serializer · serialize_AST reconstructs valid S-expression text. · `splib.h`
- [Serialized output](https://github.com/unight0/splib/blob/master/splib.h): buffer + slice · Exposes appended slice and NUL-terminated ser->output. · `splib.h`

### Build and fuzzing

- [Make build](https://github.com/unight0/splib/blob/master/Makefile): native build · Builds the example as sp and defines fuzz targets. · `Makefile`
- [AFL++ fuzzing](https://github.com/unight0/splib/blob/master/Makefile): robustness validation · Instrumented targets exercise the public parse pipeline. · `Makefile`
- **Syntax seed corpus**: fuzz inputs · Covers malformed nesting, atoms, quotes, dotted pairs, comments, and strings.
- [AFL++ dictionary](https://github.com/unight0/splib/blob/master/splib.dict): fuzz hints · Supplies token and form hints. · `splib.dict`

## Connections

- 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
