Skip to content

Parsers and conformance

Reference

Three parsers, kept in lockstep

3md has three independent parser implementations:

  • ThreeMD - the canonical Swift parser. The CLI and reference behavior are built on it.
  • A TypeScript port, published as @corvidlabs/threemd. It also backs the <three-md> web component.
  • A Rust crate, threemd, published on crates.io with zero runtime dependencies.

All three are kept behaving identically by a shared conformance suite: a set of portable test vectors that every implementation runs. The vectors, not prose, are the authoritative definition of conforming behavior. Any change that would alter their expected results is a breaking change, so a document parses the same way no matter which implementation reads it.

Each implementation also keeps its own unit tests in addition to the shared vectors.

Swift (the canonical parser)

Add the package to your Package.swift:

.package(url: "https://github.com/CorvidLabs/3md", from: "1.0.0")

Depend on the ThreeMD library product:

.product(name: "ThreeMD", package: "3md")

Then parse, inspect, and round-trip:

import ThreeMD

let document = try Parser().parse(source)
print(document.axis)            // Axis(rawValue: "time")
for plane in document.planesByZ {
    print(plane.label ?? "", plane.body)
}
print(document.danglingLinks()) // unresolved [[z=N]] references
print(document.linkGraph())     // compact source -> target edge list

// Serializing and parsing again yields an equivalent document:
let text = Serializer().render(document)

TypeScript / JavaScript

The TypeScript port is published to the public npm registry as @corvidlabs/threemd, so it installs with no token:

bun add @corvidlabs/threemd
import { danglingLinks, linkGraph, parse, serialize } from "@corvidlabs/threemd";

const document = parse(source);
console.log(document.axis);          // "time"
console.log(danglingLinks(document)); // unresolved [[z=N]] references
console.log(linkGraph(document));     // compact source -> target edge list

const text = serialize(document);     // round trips back to text

The <three-md> web component (@corvidlabs/three-md-element) builds on this parser; see the viewer docs. If you only want rendering and not the data API, the element bundle includes the parser already.

Rust

The threemd crate is on crates.io with zero runtime dependencies:

cargo add threemd
let document = threemd::parse(source)?;
println!("{}", document.axis); // "time"

Which one to use

  • Building a Swift app or the CLI: use ThreeMD.
  • Web, Node, or Bun tooling, or rendering in the browser: use the TypeScript package (or the web component).
  • Native, embedded, or performance-sensitive tooling: use the Rust crate.

Because of the shared conformance suite you can mix them freely (parse in one, validate in another) and expect identical results.

See also

  • The format - what the parsers implement.
  • Spec - the authoritative grammar and conformance contract.