Understanding the kcl-loader Module: Dependency Management in KCL

The kcl-loader module serves as the central bridge between raw KCL source files and the compiler pipeline, responsible for discovering, parsing, caching, and semantically resolving modules into a coherent dependency graph.

In the kcl-lang/kcl repository, the kcl-loader crate (located under crates/loader) orchestrates how the compiler handles external and internal dependencies. It transforms file paths into fully resolved Abstract Syntax Trees (ASTs), manages incremental compilation through sophisticated caching, and constructs the symbol tables necessary for type checking and IDE features.

What Is the kcl-loader Module?

The kcl-loader is a Rust crate that implements the load_packages function, the primary entry point for transforming KCL source code into a compiled-ready state. Unlike the kcl-parser crate, which focuses solely on syntax analysis, the loader handles the broader lifecycle of dependency management including caching, semantic resolution, and symbol collection.

Located in crates/loader/src/lib.rs, the module defines the LoadPackageOptions struct to configure loading behavior and returns a Packages struct containing the complete compilation context.

Core Responsibilities in Dependency Management

The kcl-loader module handles six critical aspects of dependency management that enable efficient and accurate compilation of KCL projects.

Module Discovery and Caching

The loader implements aggressive caching to avoid reparsing unchanged files during incremental builds. It utilizes KCLModuleCache and KCLScopeCache structures that are passed through load_program and shared with the resolver.

These caches store parsed modules and their scope information, ensuring that imported dependencies are loaded once and reused across the entire compilation run. This mechanism is essential for IDE performance and continuous integration pipelines where files change incrementally.

Parsing and AST Construction

The loader delegates initial syntax parsing to the kcl_parser crate through the load_program function. This transforms a list of file paths into a Program AST and collects any parse errors.

The resulting AST is stored in the Packages.program field, representing the complete syntactic structure of the loaded KCL code. This step establishes the foundation for all subsequent semantic analysis.

Semantic Resolution and Symbol Extraction

When LoadPackageOptions::resolve_ast is set to true (the default), the loader executes the full semantic pipeline. This involves calling resolve_program_with_opts, running the AdvancedResolver, and invoking the Namer component.

This process produces comprehensive type information, symbol tables, and scope hierarchies. The loader populates the Packages struct with symbols, scopes, and bidirectional mappings between AST nodes and symbols, enabling precise type checking and cross-reference analysis.

Built-in and External Symbol Loading

With the load_builtin option enabled, the loader injects built-in symbols from the KCL standard library into the resulting package. This makes standard library functions and types available to user code without explicit imports.

The loader handles the distinction between user-defined modules and system-provided built-ins, ensuring proper namespace isolation while maintaining accessibility.

Dependency Graph Construction

By traversing the GlobalState scope database, the loader constructs a comprehensive dependency graph between modules. It extracts symbols belonging to each file and maps package paths to their root scopes using pkg_scope_map.

This graph enables tools like the Language Server Protocol (LSP) implementation and the vet tool to locate definitions across project boundaries and understand module interdependencies.

Key Source Files and Architecture

The kcl-loader module is organized across three primary source files in the crates/loader directory:

File Role
crates/loader/src/lib.rs Core implementation containing load_packages, LoadPackageOptions, and the Packages result container.
crates/loader/src/option.rs Helper utilities for displaying available load options via list_options.
crates/loader/src/util.rs Utility functions for parsing call-argument strings and other supporting operations used during the loading process.

These files collectively implement the loader's architecture, separating configuration, core logic, and utility functions for maintainability.

Working with the kcl-loader API

The following example demonstrates how to use the kcl-loader module programmatically to load a KCL package with full semantic resolution:

use kcl_loader::{LoadPackageOptions, load_packages};

fn main() -> anyhow::Result<()> {
    // 1️⃣  Define what we want to load
    let opts = LoadPackageOptions {
        paths: vec!["example.k".into()], // KCL source files
        resolve_ast: true,               // run the full semantic pipeline
        load_builtin: true,              // include std‑lib symbols
        ..Default::default()
    };

    // 2️⃣  Load the package (parses, resolves, caches)
    let pkg = load_packages(&opts)?;

    // 3️⃣  Access the AST and symbols
    println!("AST contains {} top‑level statements.", pkg.program.stmts.len());
    println!("Discovered {} symbols.", pkg.symbols.len());

    // 4️⃣  Inspect a particular symbol (e.g., a function)
    for (sym_ref, info) in &pkg.symbols {
        println!("{} – type: {:?}", info.name, info.ty);
    }

    Ok(())
}

This example illustrates the typical workflow: configure loading options, invoke load_packages, and then access the resulting AST, symbols, and type information for further processing or tooling integration.

Summary

The kcl-loader module is the central orchestration layer for KCL's dependency management system, providing these essential capabilities:

  • Incremental compilation support through KCLModuleCache and KCLScopeCache to avoid redundant parsing
  • Complete semantic analysis via integration with the resolver, namer, and advanced resolver when resolve_ast is enabled
  • Dependency graph construction using GlobalState and pkg_scope_map to map inter-module relationships
  • Built-in symbol injection for standard library availability without explicit imports
  • Unified result packaging in the Packages struct containing AST, symbols, scopes, and error collections

By handling module discovery, caching, parsing, and semantic resolution, the kcl-loader enables the compiler and tooling ecosystem to work with coherent, fully-resolved package representations.

Frequently Asked Questions

How does kcl-loader differ from kcl-parser?

While kcl-parser focuses solely on syntactic analysis—converting source text into an Abstract Syntax Tree—the kcl-loader orchestrates the entire dependency management pipeline. It calls the parser via load_program, but additionally manages caching, semantic resolution, symbol table construction, and dependency graph generation. The loader transforms raw files into a complete Packages structure ready for compilation, whereas the parser only produces AST nodes.

What is the purpose of KCLModuleCache in the loader?

KCLModuleCache serves as the incremental compilation mechanism within the kcl-loader module. It stores previously parsed modules to avoid redundant file system operations and parsing when files haven't changed. When load_packages processes a project, it passes the cache to load_program and the resolver, ensuring that imported dependencies are shared across the entire compilation run. This significantly improves performance for large projects and IDE operations.

How does kcl-loader construct the dependency graph between modules?

The loader constructs the dependency graph by traversing the GlobalState scope database after semantic resolution. It extracts symbols belonging to each file and establishes mappings between packages and their root scopes using pkg_scope_map. This creates a clear hierarchy of module dependencies that tools like the Language Server Protocol (LSP) implementation and the vet tool use to locate definitions across project boundaries and analyze inter-module relationships.

Can I use kcl-loader without running semantic resolution?

Yes, you can disable semantic resolution by setting resolve_ast to false in LoadPackageOptions. When configured this way, the loader only performs module discovery, caching, and parsing via load_program, returning a Packages struct containing the AST and parse errors without running the AdvancedResolver, Namer, or type checking pipeline. This mode is useful when you only need syntactic information or want to defer semantic analysis to a later stage.

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 →