Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

nix-ast

A Nix library that parses, inspects, transforms, and evaluates Nix Abstract Syntax Trees (AST) using Haskell’s hnix parser.

nix-ast provides the full Nix AST API in Nix itself (via IFD, Import From Derivation) and uses hnix’s Haskell parser and evaluator internally. Parse any Nix expression into a structured AST, traverse and transform it with pure Nix functions, render it back to source code, or evaluate it, all from within your Nix expressions.


Features

FeatureDescription
ParseParse any Nix expression (file, string, or stdin) into a structured, typed AST
RenderConvert an AST back to formatted, importable Nix code
EvalEvaluate ASTs using hnix’s evaluator and get the results as JSON values
toASTConvert native Nix values (ints, strings, lists, attrsets) to AST nodes (pure, no IFD)
fromASTConvert an AST back to a native Nix value, the inverse of toAST (pure, no IFD)
Pattern MatchingType-safe tag-based dispatch with match ast { Tag = handler; _ = fallback; }
TraversalRecursive transform, rewrite, universe, children/rebuild, and contexts
Type-checked ConstructorsAll mk* builders validate argument types at runtime with descriptive error messages
CLI Toolnix-ast eval / nix-ast parse / nix-ast render for shell/pipe workflows

Quick Start

Using the Nix Library (Flake)

Add nix-ast to your flake inputs:

inputs.nix-ast.url = "github:z1-0/nix-ast";

The library provides three main API groups via inputs.nix-ast.lib:

APIDescription
lib.parse lib.render lib.evalIFD-based: bridge to hnix’s parser/evaluator
lib.toAST lib.fromASTPure Nix: convert between Nix values and ASTs
lib.match lib.syntax lib.traversalWork 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;

CLI

# Parse from expression string
nix-ast parse --expr '{ x = 1; }' > ast.json

# Parse files from stdin (JSON array of paths)
cat paths.json | nix-ast parse > asts.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
cat paths.json | nix-ast parse | nix-ast eval

Architecture

┌─────────────────────────────────────────────────────────┐
│                nix-ast                                  │
│                                                         │
│  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.

Installation

Use nix-ast as a CLI tool or as a Nix library.

CLI (run directly, no installation)

# Run directly without installing
nix run github:z1-0/nix-ast -- parse --expr '{ x = 1; }'

Nix library (via flake inputs)

# Add to your flake inputs
inputs.nix-ast.url = "github:z1-0/nix-ast";

See Quick Start for the library API (lib.parse, lib.render, …).

Binary Cache

Use the Cachix cache to skip building from source: the binary is built against a patched hnix 0.17, so its derivation doesn’t match nixpkgs’ hnix, and nixpkgs’ cache can’t substitute it.

Quickest way to enable it:

cachix use z1-0

Or add it to your NixOS/home-manager config:

nix.settings = {
  substituters = [ "https://z1-0.cachix.org" ];
  trusted-public-keys = [ "z1-0.cachix.org-1:FS7lPgL0StRBOPrlu0RgdCL7LafUI23+U6Iivdw5QK8=" ];
};

Learn more

  • API Reference: parse, render, eval, construct, match, and traverse AST nodes
  • AST Reference: every node type with fields, JSON representation, and examples
    • Expr: 18 expression constructors
    • Atom: primitive constants (Bool, Int, Float, Null, Uri)
    • Binding: Inherit, NamedVar
    • KeyName: StaticKey, DynamicKey
    • Params: Param, ParamSet
    • String: DoubleQuoted, Indented
    • Antiquoted: Plain, Antiquoted, EscapedNewline
  • CLI Reference: nix-ast eval / nix-ast parse / nix-ast render

License

BSD-3-Clause: see LICENSE for details.