Convert between JSON, JSONC/JSON5, TOML, YAML, and more—while preserving comments!
fig lets you do a lot of things with config languages that weren’t previously possible:
$ fig get config.yaml
hello: world
$ fig comment config.yaml --inline hello "this is a comment"
$ fig get config.yaml
hello: world # this is a comment
$ fig get config.yaml -o jsonc
{
"hello": "world" // this is a comment
}
$ fig edit config.yaml hello everyone
$ fig get config.yaml -o toml
hello = "everyone" # this is a comment
I’ve learned so much about abstract syntax trees, serialization and deserialization, side tables, etc. and just had a lot of fun making this. I hope it can help someone too!
Supported Zig versions
0.16.0 only
AI / LLM usage disclosure
I used LLMs to help me achieve conformance for the various configuration languages. I wrote the JSON tokenizer/parser by hand, and designed the AST, Embed, Language, Editor, and Token abstractions myself. LLMs helped me learn the basics of Zig, how C APIs and bindings worked, and helped guide me throughout the process as I wrote code myself. Lately, I’ve been using it to write more mundane code, but keep it on a short leash and review all the changes. Let me know if you have questions!
YAML is famously a beast to properly parse. The documentation is incredibly long. It even has its own programming language now. Are you using a YAML parser or wrote one yourself?
Wrote myself (with LLM help). Fig is completely dependency-free besides the standard library. (And I sort of HAD to write it myself, because Fig also provides comment-preserving editing for each language it supports, which requires first-class access to its AST, and I didn’t know of any libraries that were that hackable.)
I initially approached it optimistically knowing that they apparently “simplified” the spec in v1.2.2. Still a beast. Took quite a bit of experimentation to wrap my head around indents/dedents for the tokenizer, and was surprised to find how many exceptions there were (list continuations, next-line scalars, etc.). It helped that my AST abstraction was mostly pinned down, but I had to add side tables for the alias/anchor system (which allows YAML to represent cyclical data structures, something that no other config language I know of can do), and converting to other config languages required a “materialization” policy—if an alias is referenced, should I copy the data, delete it, or something else?
It also helped that there was a test corpus: GitHub - yaml/yaml-test-suite: Comprehensive, language independent Test Suite for YAML · GitHub Unfortunately, covering all of those cases still didn’t guarantee 100% YAML conformance—I still find myself fixing bugs and performance issues occasionally. I’m still not really sure when to claim spec-compliance. But I think I am mostly there, and if anyone in the Zig community needs a decent YAML parser they can clone/fetch Fig and do zig build -Djson=false -Dtoml=false -Dzon=false -Dyaml=true to just get the YAML portion.