Add operations for variable declarations (#7451)

* Add operations for variable declarations

* Update output

* Change to use OpKclValue

* Update output after merge
This commit is contained in:
Jonathan Tran
2025-06-12 12:38:12 -04:00
committed by GitHub
parent e0025f7fad
commit 383b38c2d2
224 changed files with 71646 additions and 462 deletions

View File

@ -4,7 +4,7 @@ use serde::Serialize;
use super::{types::NumericType, ArtifactId, KclValue};
#[cfg(feature = "artifact-graph")]
use crate::parsing::ast::types::{Node, Program};
use crate::{ModuleId, NodePath, SourceRange};
use crate::{parsing::ast::types::ItemVisibility, ModuleId, NodePath, SourceRange};
/// A CAD modeling operation for display in the feature tree, AKA operations
/// timeline.
@ -28,6 +28,20 @@ pub enum Operation {
is_error: bool,
},
#[serde(rename_all = "camelCase")]
VariableDeclaration {
/// The variable name.
name: String,
/// The value of the variable.
value: OpKclValue,
/// The visibility modifier of the variable, e.g. `export`. `Default`
/// means there is no visibility modifier.
visibility: ItemVisibility,
/// The node path of the operation in the source code.
node_path: NodePath,
/// The source range of the operation in the source code.
source_range: SourceRange,
},
#[serde(rename_all = "camelCase")]
GroupBegin {
/// The details of the group.
group: Group,
@ -44,7 +58,7 @@ impl Operation {
pub(crate) fn set_std_lib_call_is_error(&mut self, is_err: bool) {
match self {
Self::StdLibCall { ref mut is_error, .. } => *is_error = is_err,
Self::GroupBegin { .. } | Self::GroupEnd => {}
Self::VariableDeclaration { .. } | Self::GroupBegin { .. } | Self::GroupEnd => {}
}
}
@ -56,6 +70,11 @@ impl Operation {
source_range,
..
}
| Operation::VariableDeclaration {
node_path,
source_range,
..
}
| Operation::GroupBegin {
node_path,
source_range,

View File

@ -6,13 +6,14 @@ use crate::{
errors::{KclError, KclErrorDetails},
execution::{
annotations,
cad_op::OpKclValue,
fn_call::Args,
kcl_value::{FunctionSource, TypeDef},
memory,
state::ModuleState,
types::{NumericType, PrimitiveType, RuntimeType},
BodyType, EnvironmentRef, ExecState, ExecutorContext, KclValue, Metadata, ModelingCmdMeta, ModuleArtifactState,
PlaneType, StatementKind, TagIdentifier,
Operation, PlaneType, StatementKind, TagIdentifier,
},
fmt,
modules::{ModuleId, ModulePath, ModuleRepr},
@ -24,7 +25,7 @@ use crate::{
},
source_range::SourceRange,
std::args::TyF64,
CompilationError,
CompilationError, NodePath,
};
impl<'a> StatementKind<'a> {
@ -329,6 +330,16 @@ impl ExecutorContext {
.mut_stack()
.add(var_name.clone(), rhs.clone(), source_range)?;
if rhs.show_variable_in_feature_tree() {
exec_state.push_op(Operation::VariableDeclaration {
name: var_name.clone(),
value: OpKclValue::from(&rhs),
visibility: variable_declaration.visibility,
node_path: NodePath::placeholder(),
source_range,
});
}
// Track exports.
if let ItemVisibility::Export = variable_declaration.visibility {
if matches!(body_type, BodyType::Root) {

View File

@ -277,6 +277,31 @@ impl KclValue {
}
}
/// Returns true if we should generate an [`crate::execution::Operation`] to
/// display in the Feature Tree for variable declarations initialized with
/// this value.
pub(crate) fn show_variable_in_feature_tree(&self) -> bool {
match self {
KclValue::Uuid { .. } => false,
KclValue::Bool { .. } | KclValue::Number { .. } | KclValue::String { .. } => true,
KclValue::Tuple { .. }
| KclValue::HomArray { .. }
| KclValue::Object { .. }
| KclValue::TagIdentifier(_)
| KclValue::TagDeclarator(_)
| KclValue::Plane { .. }
| KclValue::Face { .. }
| KclValue::Sketch { .. }
| KclValue::Solid { .. }
| KclValue::Helix { .. }
| KclValue::ImportedGeometry(_)
| KclValue::Function { .. }
| KclValue::Module { .. }
| KclValue::Type { .. }
| KclValue::KclNone { .. } => false,
}
}
/// Human readable type name used in error messages. Should not be relied
/// on for program logic.
pub(crate) fn human_friendly_type(&self) -> String {

View File

@ -8,7 +8,8 @@ pub use artifact::{Artifact, ArtifactCommand, ArtifactGraph, CodeRef, StartSketc
use cache::GlobalState;
pub use cache::{bust_cache, clear_mem_cache};
#[cfg(feature = "artifact-graph")]
pub use cad_op::{Group, Operation};
pub use cad_op::Group;
pub use cad_op::Operation;
pub use geometry::*;
pub use id_generator::IdGenerator;
pub(crate) use import::PreImportedGeometry;
@ -1175,6 +1176,9 @@ impl ExecutorContext {
/// SAFETY: the current thread must have sole access to the memory referenced in exec_state.
async fn eval_prelude(&self, exec_state: &mut ExecState, source_range: SourceRange) -> Result<(), KclError> {
if exec_state.stack().memory.requires_std() {
#[cfg(feature = "artifact-graph")]
let initial_ops = exec_state.global.artifacts.operations.len();
let path = vec!["std".to_owned(), "prelude".to_owned()];
let resolved_path = ModulePath::from_std_import_path(&path)?;
let id = self
@ -1183,6 +1187,14 @@ impl ExecutorContext {
let (module_memory, _) = self.exec_module_for_items(id, exec_state, source_range).await?;
exec_state.mut_stack().memory.set_std(module_memory);
// Operations generated by the prelude are not useful, so clear them
// out.
//
// TODO: Should we also clear them out of each module so that they
// don't appear in test output?
#[cfg(feature = "artifact-graph")]
exec_state.global.artifacts.operations.truncate(initial_ops);
}
Ok(())