kcl-lang / kcl-lang.io

KCL Website and Documentation Repo: https://kcl-lang.io
https://kcl-lang.github.io
Apache License 2.0
15 stars 34 forks source link

[FAQ] How to get object references? (Rust SDK) #397

Open patrycju opened 4 months ago

patrycju commented 4 months ago

How to get object references? (Rust SDK)

Hey! Hope you're doing well! I have a quick questions surrounding Rust SDK.

Let's say I have following KCL file:

schema File:
  name: str
  path: Path

schema Path:
  p: str

ext = "txt"
file_name = "hello.${ext}"
path = Path { p = "/tmp" }

f = File { name = file_name, path = path }

I need to extract references to other identifiers used in any field or value of given object, so for example, based on above file I need to get:

f -> ["file_name", "path"]
file_name -> ["ext"]

Now, I checked Rust SDK and even tried to find it in non-public APIs like Parser or Evaluator, but no luck. I'm slowly learning internals of KCL, but it's very complex and I'm feeling little lost.

Can you please share where can I get this information or at what level should I intercept compilation to get this?

Thank you in advance!

Peefy commented 4 months ago

Hello @patrycju At present, there is no such advanced APIs in KCL, but code can be written to complete it. The usual way is to use the loader module to obtain the syntax tree and semantic information, which can be obtained from the syntax tree or semantic information. I have written a simple unfinished example list_var_deps as follows

use anyhow::Result;
use std::collections::HashMap;
use kclvm_ast::{ast, walker::MutSelfWalker};
use kclvm_loader::{load_packages, LoadPackageOptions};

#[derive(Debug, Default)]
struct VarDepsExtractor {
    pkgpath: String,
    deps: HashMap<String, Vec<String>>,
    current_var: Option<String>,
}

impl MutSelfWalker for VarDepsExtractor {
    fn walk_identifier(&mut self, identifier: &ast::Identifier) {
        match identifier.ctx {
            ast::ExprContext::Load => todo!(), // Store the deps for the current var
            ast::ExprContext::Store => todo!(),
        }
    }
    fn walk_assign_stmt(&mut self, assign_stmt: &ast::AssignStmt) {
        for target in &assign_stmt.targets {
            self.current_var = target.node.names.first().map(|v|&v.node).cloned();
            self.walk_expr(&assign_stmt.value.node)
        }
    }
}

pub fn list_var_deps(opts: &LoadPackageOptions) -> Result<HashMap<String, Vec<String>>> {
    let packages = load_packages(opts)?;
    let mut extractor = VarDepsExtractor::default();
    for (pkgpath, modules) in &packages.program.pkgs {
        extractor.pkgpath = pkgpath.clone();
        for module in modules {
            extractor.walk_module(module)
        }
    }
    Ok(extractor.deps)
}

Here's another case to get all option functions in the KCL code.

Ref: https://github.com/kcl-lang/kcl/blob/main/kclvm/loader/src/option.rs