A Nix library that parses, inspects, transforms, and evaluates Nix Abstract Syntax Trees (AST) using Haskell's hnix parser.
nix-ast exposes a full Nix AST API directly in Nix itself (via IFD, Import From Derivation) and uses hnix's Haskell parser and evaluator internally. You can parse any Nix expression into a structured AST, traverse and transform it with pure Nix functions, render it back to source code, and even evaluate it. All from within your Nix expressions.
| Feature | Description |
|---|---|
| Parse | Parse any Nix expression (file, string, or stdin) into a structured, typed AST |
| Render | Convert an AST back to formatted, importable Nix code |
| Eval | Evaluate ASTs using hnix's evaluator and get the results as JSON values |
| toAST | Convert native Nix values (ints, strings, lists, attrsets) to AST nodes (pure, no IFD) |
| fromAST | Convert an AST back to a native Nix value, the inverse of toAST (pure, no IFD) |
| Pattern Matching | Type-safe tag-based dispatch with match ast { Tag = handler; _ = fallback; } |
| Traversal | Recursive transform, rewrite, universe, children/rebuild, and contexts |
| Type-checked Constructors | All mk* builders validate argument types at runtime with descriptive error messages |
| CLI Tool | nix-ast eval / nix-ast parse / nix-ast render for shell/pipe workflows |
Add nix-ast to your flake inputs:
inputs.nix-ast.url = "github:z1-0/nix-ast";The library exposes three main API groups via inputs.nix-ast.lib:
| API | Description |
|---|---|
lib.parse lib.render lib.eval |
IFD-based: bridge to hnix's parser/evaluator |
lib.toAST lib.fromAST |
Pure Nix: convert between Nix values and ASTs |
lib.match lib.syntax lib.traversal |
Work with AST nodes after construction |
# Parse -> Transform -> Render
asts = lib.parse pkgs [./config.nix];
transformed = lib.traversal.transform (node: ...) (builtins.head asts);
outPaths = lib.render pkgs [transformed];
config = import (builtins.head outPaths);
# Parse -> Evaluate (skip rendering)
result = lib.eval pkgs asts;# Parse from expression string
nix-ast parse --expr '{ x = 1; }' > ast.json
# Parse files from stdin (one path per line)
echo ./config.nix | nix-ast parse > ast.json
# Render AST back to Nix
nix-ast render --json '{"tag":"Set","recursive":false,"bindings":[...]}'
# Render batch to output directory
nix-ast render < asts.json --out-dir ./out
# Evaluate AST using hnix
nix-ast eval --json '{"tag":"Constant","contents":{"tag":"Int","contents":42}}'
# Pipe workflow: parse -> eval
nix-ast parse --expr '{ x = 1 + 2; }' | nix-ast eval┌─────────────────────────────────────────────────────────┐
│ Nix (your flake) │
│ │
│ lib.parse lib.render lib.eval │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────────────────────────────┐ │
│ │ IFD: nix-ast CLI (Haskell) │ <- hnix parser/eval │
│ └──────────────────────────────┘ │
│ │
│ lib.toAST lib.fromAST (pure Nix, no IFD) │
│ lib.match lib.syntax lib.traversal │
└─────────────────────────────────────────────────────────┘
parse/render/eval go through Haskell via IFD. toAST/fromAST/match/syntax/traversal run in pure Nix on the AST values.
# Run directly without installing
nix run github:z1-0/nix-ast -- parse --expr '{ x = 1; }'
# Or add to your flake inputs
inputs.nix-ast.url = "github:z1-0/nix-ast";Use the Cachix cache to skip building from source.
nix.settings = {
substituters = [ "https://z1-0.cachix.org" ];
trusted-public-keys = [ "z1-0.cachix.org-1:FS7lPgL0StRBOPrlu0RgdCL7LafUI23+U6Iivdw5QK8=" ];
};- API Reference: parse, render, eval, construct, match, and traverse AST nodes
- Core Functions:
parse,render,eval,toAST,fromAST - Pattern Matching: tag-based dispatch on node types
- Syntax: runtime-validated constructors and predicates
- Traversal: children, rebuild, transform, universe
- Core Functions:
- AST Reference: every node type with fields, JSON representation, and examples
- CLI Reference:
nix-ast eval/nix-ast parse/nix-ast render
BSD-3-Clause: see LICENSE for details.