# kcl-ast vs kcl-ast-pretty: Understanding the Distinction in KCL's Compiler

> Explore the difference between kcl-ast and kcl-ast-pretty in KCL. Learn how kcl-ast defines syntax trees and kcl-ast-pretty formats them for readability and debugging.

- Repository: [The KCL Programming Language/kcl](https://github.com/kcl-lang/kcl)
- Tags: internals
- Published: 2026-03-05

---

**The `kcl-ast` crate defines the core abstract syntax tree data structures that represent parsed KCL source code, while `kcl-ast-pretty` provides formatting utilities that convert these AST nodes into human-readable strings for debugging and tooling purposes.**

The `kcl-lang/kcl` repository organizes its Rust compiler into discrete crates with single responsibilities. Two foundational crates—**`kcl-ast`** and **`kcl-ast-pretty`**—handle the representation and visualization of KCL programs. Understanding their separation is critical for developers building compiler passes, language tools, or IDE integrations.

## Core Purpose of kcl-ast

The **`kcl-ast`** crate serves as the **data model layer** for the entire compiler pipeline. Located in `crates/ast`, this crate contains the canonical definitions of every node type that represents KCL source code after parsing.

Key responsibilities include:

- Defining fundamental types like `Node<T>`, `Pos`, `AstIndex`, `Program`, `Module`, `Stmt`, and `Expr`
- Providing low-level utilities for creating, cloning, and serializing AST nodes via `serde`
- Acting as the immutable structure that the parser builds and subsequent phases (type-checker, optimizer) manipulate

According to the source code in [`crates/ast/src/ast.rs`](https://github.com/kcl-lang/kcl/blob/main/crates/ast/src/ast.rs), the `Node<T>` struct wraps every AST element with positional metadata, enabling precise error reporting throughout the compiler. This crate deliberately avoids any presentation logic to maintain a minimal dependency tree.

## Core Purpose of kcl-ast-pretty

The **`kcl-ast-pretty`** crate implements the **presentation layer** for the AST. Located in `crates/ast_pretty`, it transforms the raw tree structures from `kcl-ast` into formatted, indented output suitable for human consumption.

Key functions include:

- **`print_ast_module`**: Renders an entire `Program` or `Module` as a formatted string
- **`print_ast_node`**: Converts individual `Node<T>` instances to readable text
- Supporting debug output and diagnostic formatting for the LSP and CLI tools

As implemented in `kcl-ast-pretty`, the crate depends on `kcl-ast` to access node definitions and uses `serde_json` for structured output formatting. The `kcl format` command and various test suites consume this crate to display program state without modifying the underlying AST.

## Key Differences Between kcl-ast and kcl-ast-pretty

While both crates handle AST representation, they serve distinct architectural roles:

**Dependencies and Relationships**
- **`kcl-ast`** sits at the bottom of the dependency graph. It depends only on core libraries like `serde`, `uuid`, and `compiler_base_span`. No other AST-related crates depend on it cyclically.
- **`kcl-ast-pretty`** explicitly depends on `kcl-ast` (declared in [`crates/ast_pretty/Cargo.toml`](https://github.com/kcl-lang/kcl/blob/main/crates/ast_pretty/Cargo.toml)) plus formatting utilities like `serde_json` and `pretty_assertions` for testing.

**Data vs. Presentation**
- **`kcl-ast`** stores the *structure*—the what of the program (expressions, statements, literals).
- **`kcl-ast-pretty`** handles the *view*—the how of displaying that structure to developers through formatted strings or JSON.

**Compilation Phase Usage**
- The parser (`crates/parser`) constructs `kcl-ast` nodes during the initial parse phase.
- The semantic analyzer (`crates/sema`) and resolver manipulate these same structures.
- Pretty-printing occurs optionally during formatting ([`crates/tools/src/format/mod.rs`](https://github.com/kcl-lang/kcl/blob/main/crates/tools/src/format/mod.rs)) or debugging, walking the existing tree to emit text without altering nodes.

## Architecture Flow: How They Work Together

The separation creates a clean pipeline from source code to human-readable output:

1. **Parsing**: The parser reads `.k` files and constructs `kcl-ast` nodes (`Node<T>`, `Expr`, `Stmt`) defined in [`crates/ast/src/ast.rs`](https://github.com/kcl-lang/kcl/blob/main/crates/ast/src/ast.rs).
2. **Analysis**: The resolver and type-checker annotate or transform these structures, still operating purely on `kcl-ast` types.
3. **Visualization**: When the `kcl fmt --print-ast` command runs or the LSP needs to display a tree, [`crates/tools/src/format/mod.rs`](https://github.com/kcl-lang/kcl/blob/main/crates/tools/src/format/mod.rs) imports `kcl_ast_pretty::print_ast_module` to render the view.

This split ensures that core compiler logic remains lightweight, while tools like `kcl-query`, `kcl-loader`, and the LSP can pull in formatting capabilities only when needed.

## Code Examples

### Building AST Nodes with kcl-ast

The following example demonstrates creating an AST node using the core data structures:

```rust
use kcl_ast::node::Node;
use kcl_ast::ast::{Expr, NumberLit};

// Create a literal integer expression node
let expr = Expr::NumberLit(NumberLit { value: 42.0, suffix: None });
let node = Node::new(
    expr,
    "example.k".to_string(),
    1,   // line
    5,   // column
    1,   // end_line
    7,   // end_column
);

```

### Pretty-Printing with kcl-ast-pretty

Convert loaded programs to readable format using the pretty-printing utilities:

```rust
use kcl_ast_pretty::print_ast_module;
use kcl_loader::load_program;

// Load a KCL program (returns a `Program` node)
let program = load_program("example.k").unwrap();

// Produce a nicely indented representation
let pretty = print_ast_module(&program);
println!("{}", pretty);

```

### Integration in CLI Tools

The formatter tool demonstrates real-world usage within the KCL codebase:

```rust
// Inside crates/tools/src/format/mod.rs
use kcl_ast_pretty::print_ast_module;
use kcl_ast::ast::Program;

pub fn format_and_print(program: &Program) -> String {
    // Apply any formatting passes, then pretty-print
    print_ast_module(program)
}

```

## Summary

- **`kcl-ast`** defines the foundational data structures (`Node<T>`, `Program`, `Expr`) used throughout the KCL compiler pipeline, located in [`crates/ast/src/ast.rs`](https://github.com/kcl-lang/kcl/blob/main/crates/ast/src/ast.rs).
- **`kcl-ast-pretty`** provides human-readable formatting via `print_ast_module` and `print_ast_node`, residing in [`crates/ast_pretty/src/lib.rs`](https://github.com/kcl-lang/kcl/blob/main/crates/ast_pretty/src/lib.rs).
- The **dependency relationship** flows one way: `kcl-ast-pretty` requires `kcl-ast`, but the core crate remains independent.
- **Use `kcl-ast`** when building compiler passes or analyzing code structure; **use `kcl-ast-pretty`** when implementing debugging tools, formatters, or diagnostic displays.

## Frequently Asked Questions

### Can kcl-ast be used independently of kcl-ast-pretty?

Yes. The `kcl-ast` crate has no dependency on `kcl-ast-pretty` and is used throughout the compiler for parsing, type-checking, and code generation without ever invoking pretty-printing logic. This design keeps the core compiler lightweight.

### Does kcl-ast-pretty modify AST nodes during formatting?

No. Functions like `print_ast_module` take references to AST nodes and produce **String** output without mutating the underlying `Node<T>` structures. The crate is purely a read-only viewer of the tree.

### Which crate should I import when building a custom formatter?

Import both. Your tool should use `kcl-ast` to represent the program structure and `kcl-ast-pretty` to generate the final formatted output, as demonstrated in [`crates/tools/src/format/mod.rs`](https://github.com/kcl-lang/kcl/blob/main/crates/tools/src/format/mod.rs) where the formatter applies transformations then calls `print_ast_module`.

### Where are these crates declared in the KCL workspace?

Both crates are workspace members defined in the root [`Cargo.toml`](https://github.com/kcl-lang/kcl/blob/main/Cargo.toml). `kcl-ast` is declared as `kcl-ast = { path = "crates/ast" }`, while `kcl-ast-pretty` uses `kcl-ast-pretty = { path = "crates/ast_pretty" }` and is re-exported via `kcl-ast-pretty.workspace = true` for consumption by other compiler crates.