Skip to main content

README.md

# atproto_mlf

> **Alpha.** Pre-1.0 and evolving; expect breaking changes between 0.x
> releases. Upstream [mlf](https://mlf.lol/) is itself pre-release.

Parse and print [mlf](https://mlf.lol/) ("Matt's Lexicon Format") against the
[`atproto_lexicon`](https://hex.pm/packages/atproto_lexicon) AST, with a
bidirectional CLI for converting whole lexicon file trees. A clean-room Gleam
implementation of the upstream grammar, oracle-tested against upstream's own
snapshot fixtures.

## Installation

```sh
gleam add atproto_mlf
```

## Usage

Library:

```gleam
import atproto_mlf

let assert Ok(doc) = atproto_mlf.parse(source, "com.example.thread")
// doc is an atproto_lexicon ast.LexiconDoc; encode it, diff it, feed codegen.
```

CLI (walks both `.mlf` and `.json` inputs; four directions from one model:
compile, convert, normalize, fmt):

```sh
gleam run -m atproto_mlf -- <src-dir> <out-dir> [--to json|mlf]
```

NSIDs derive from file paths (`com/example/thread.mlf` becomes
`com.example.thread`). Constructs the lexicon spec allows but mlf cannot
express (e.g. a procedure with both query parameters and a non-JSON input
body) produce a spanned error naming the construct; nothing is ever silently
dropped.

## Architecture

| Module    | Role                                                     |
| --------- | -------------------------------------------------------- |
| `lexer`   | graphemes to spanned tokens (`///` docs are significant) |
| `parser`  | recursive descent to an mlf tree                         |
| `lower`   | mlf tree to `atproto_lexicon` AST; ref resolution        |
| `printer` | AST to vanilla mlf text; total or `PrintError`, no loss  |
| `cli`     | bidirectional tree converter                             |