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

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, 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) 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) 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.
  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 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:

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:

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:

// 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.
  • kcl-ast-pretty provides human-readable formatting via print_ast_module and print_ast_node, residing in 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 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. 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →