Compare commits
	
		
			33 Commits
		
	
	
		
			kcl-80
			...
			jess/chang
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 495727d617 | |||
| e943303434 | |||
| 8ce175f006 | |||
| 13aa178734 | |||
| de137ff13b | |||
| 3504b9246f | |||
| a035f7879b | |||
| d122d7a224 | |||
| 198e7c4bd2 | |||
| 14d8903acc | |||
| 275c23f294 | |||
| 0ac9ac3896 | |||
| 0220d0f9de | |||
| 4523dc209b | |||
| ea73eb011c | |||
| 0aa2824c20 | |||
| e66893c5d0 | |||
| 60274127df | |||
| a0d1750829 | |||
| 00ffa8c0bf | |||
| fafdf41093 | |||
| a2092e7ed6 | |||
| aa103d299c | |||
| aaaab495bc | |||
| 364e38fda2 | |||
| b085af139b | |||
| ec537cd8dc | |||
| 8debbc5241 | |||
| a590ed99cf | |||
| a002bb60a0 | |||
| 6ba01b8dfa | |||
| ca9e6e0944 | |||
| e85e54215c | 
							
								
								
									
										20
									
								
								rust/Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										20
									
								
								rust/Cargo.lock
									
									
									
										generated
									
									
									
								
							| @ -1907,6 +1907,7 @@ dependencies = [ | ||||
|  "serde_json", | ||||
|  "sha2", | ||||
|  "tabled", | ||||
|  "tempdir", | ||||
|  "thiserror 2.0.12", | ||||
|  "tokio", | ||||
|  "tokio-tungstenite", | ||||
| @ -3084,6 +3085,15 @@ version = "0.8.5" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" | ||||
|  | ||||
| [[package]] | ||||
| name = "remove_dir_all" | ||||
| version = "0.5.3" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" | ||||
| dependencies = [ | ||||
|  "winapi", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "reqwest" | ||||
| version = "0.12.15" | ||||
| @ -3779,6 +3789,16 @@ version = "0.13.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a" | ||||
|  | ||||
| [[package]] | ||||
| name = "tempdir" | ||||
| version = "0.3.7" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" | ||||
| dependencies = [ | ||||
|  "rand 0.4.6", | ||||
|  "remove_dir_all", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "tempfile" | ||||
| version = "3.19.0" | ||||
|  | ||||
| @ -22,7 +22,6 @@ debug = 0 | ||||
|  | ||||
| [profile.dev.package] | ||||
| insta = { opt-level = 3 } | ||||
| similar = { opt-level = 3 } | ||||
|  | ||||
| [profile.test] | ||||
| debug = "line-tables-only" | ||||
|  | ||||
| @ -69,6 +69,7 @@ serde = { workspace = true } | ||||
| serde_json = { workspace = true } | ||||
| sha2 = "0.10.8" | ||||
| tabled = { version = "0.18.0", optional = true } | ||||
| tempdir = "0.3.7" | ||||
| thiserror = "2.0.0" | ||||
| toml = "0.8.19" | ||||
| ts-rs = { version = "10.1.0", features = [ | ||||
|  | ||||
| @ -18,7 +18,7 @@ use tokio::sync::{mpsc, oneshot, RwLock}; | ||||
| use tokio_tungstenite::tungstenite::Message as WsMsg; | ||||
| use uuid::Uuid; | ||||
|  | ||||
| use super::{EngineStats, ExecutionKind}; | ||||
| use super::EngineStats; | ||||
| use crate::{ | ||||
|     engine::EngineManager, | ||||
|     errors::{KclError, KclErrorDetails}, | ||||
| @ -51,7 +51,6 @@ pub struct EngineConnection { | ||||
|     /// If the server sends session data, it'll be copied to here. | ||||
|     session_data: Arc<RwLock<Option<ModelingSessionData>>>, | ||||
|  | ||||
|     execution_kind: Arc<RwLock<ExecutionKind>>, | ||||
|     stats: EngineStats, | ||||
| } | ||||
|  | ||||
| @ -344,7 +343,6 @@ impl EngineConnection { | ||||
|             artifact_commands: Arc::new(RwLock::new(Vec::new())), | ||||
|             default_planes: Default::default(), | ||||
|             session_data, | ||||
|             execution_kind: Default::default(), | ||||
|             stats: Default::default(), | ||||
|         }) | ||||
|     } | ||||
| @ -368,18 +366,6 @@ impl EngineManager for EngineConnection { | ||||
|         self.artifact_commands.clone() | ||||
|     } | ||||
|  | ||||
|     async fn execution_kind(&self) -> ExecutionKind { | ||||
|         let guard = self.execution_kind.read().await; | ||||
|         *guard | ||||
|     } | ||||
|  | ||||
|     async fn replace_execution_kind(&self, execution_kind: ExecutionKind) -> ExecutionKind { | ||||
|         let mut guard = self.execution_kind.write().await; | ||||
|         let original = *guard; | ||||
|         *guard = execution_kind; | ||||
|         original | ||||
|     } | ||||
|  | ||||
|     fn stats(&self) -> &EngineStats { | ||||
|         &self.stats | ||||
|     } | ||||
|  | ||||
| @ -16,7 +16,7 @@ use kittycad_modeling_cmds::{self as kcmc}; | ||||
| use tokio::sync::RwLock; | ||||
| use uuid::Uuid; | ||||
|  | ||||
| use super::{EngineStats, ExecutionKind}; | ||||
| use super::EngineStats; | ||||
| use crate::{ | ||||
|     errors::KclError, | ||||
|     exec::DefaultPlanes, | ||||
| @ -29,7 +29,6 @@ pub struct EngineConnection { | ||||
|     batch: Arc<RwLock<Vec<(WebSocketRequest, SourceRange)>>>, | ||||
|     batch_end: Arc<RwLock<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>>, | ||||
|     artifact_commands: Arc<RwLock<Vec<ArtifactCommand>>>, | ||||
|     execution_kind: Arc<RwLock<ExecutionKind>>, | ||||
|     /// The default planes for the scene. | ||||
|     default_planes: Arc<RwLock<Option<DefaultPlanes>>>, | ||||
|     stats: EngineStats, | ||||
| @ -41,7 +40,6 @@ impl EngineConnection { | ||||
|             batch: Arc::new(RwLock::new(Vec::new())), | ||||
|             batch_end: Arc::new(RwLock::new(IndexMap::new())), | ||||
|             artifact_commands: Arc::new(RwLock::new(Vec::new())), | ||||
|             execution_kind: Default::default(), | ||||
|             default_planes: Default::default(), | ||||
|             stats: Default::default(), | ||||
|         }) | ||||
| @ -70,18 +68,6 @@ impl crate::engine::EngineManager for EngineConnection { | ||||
|         self.artifact_commands.clone() | ||||
|     } | ||||
|  | ||||
|     async fn execution_kind(&self) -> ExecutionKind { | ||||
|         let guard = self.execution_kind.read().await; | ||||
|         *guard | ||||
|     } | ||||
|  | ||||
|     async fn replace_execution_kind(&self, execution_kind: ExecutionKind) -> ExecutionKind { | ||||
|         let mut guard = self.execution_kind.write().await; | ||||
|         let original = *guard; | ||||
|         *guard = execution_kind; | ||||
|         original | ||||
|     } | ||||
|  | ||||
|     fn get_default_planes(&self) -> Arc<RwLock<Option<DefaultPlanes>>> { | ||||
|         self.default_planes.clone() | ||||
|     } | ||||
|  | ||||
| @ -11,7 +11,7 @@ use uuid::Uuid; | ||||
| use wasm_bindgen::prelude::*; | ||||
|  | ||||
| use crate::{ | ||||
|     engine::{EngineStats, ExecutionKind}, | ||||
|     engine::EngineStats, | ||||
|     errors::{KclError, KclErrorDetails}, | ||||
|     execution::{ArtifactCommand, DefaultPlanes, IdGenerator}, | ||||
|     SourceRange, | ||||
| @ -42,7 +42,6 @@ pub struct EngineConnection { | ||||
|     batch_end: Arc<RwLock<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>>, | ||||
|     responses: Arc<RwLock<IndexMap<Uuid, WebSocketResponse>>>, | ||||
|     artifact_commands: Arc<RwLock<Vec<ArtifactCommand>>>, | ||||
|     execution_kind: Arc<RwLock<ExecutionKind>>, | ||||
|     /// The default planes for the scene. | ||||
|     default_planes: Arc<RwLock<Option<DefaultPlanes>>>, | ||||
|     stats: EngineStats, | ||||
| @ -61,7 +60,6 @@ impl EngineConnection { | ||||
|             batch_end: Arc::new(RwLock::new(IndexMap::new())), | ||||
|             responses: Arc::new(RwLock::new(IndexMap::new())), | ||||
|             artifact_commands: Arc::new(RwLock::new(Vec::new())), | ||||
|             execution_kind: Default::default(), | ||||
|             default_planes: Default::default(), | ||||
|             stats: Default::default(), | ||||
|         }) | ||||
| @ -164,18 +162,6 @@ impl crate::engine::EngineManager for EngineConnection { | ||||
|         self.artifact_commands.clone() | ||||
|     } | ||||
|  | ||||
|     async fn execution_kind(&self) -> ExecutionKind { | ||||
|         let guard = self.execution_kind.read().await; | ||||
|         *guard | ||||
|     } | ||||
|  | ||||
|     async fn replace_execution_kind(&self, execution_kind: ExecutionKind) -> ExecutionKind { | ||||
|         let mut guard = self.execution_kind.write().await; | ||||
|         let original = *guard; | ||||
|         *guard = execution_kind; | ||||
|         original | ||||
|     } | ||||
|  | ||||
|     fn get_default_planes(&self) -> Arc<RwLock<Option<DefaultPlanes>>> { | ||||
|         self.default_planes.clone() | ||||
|     } | ||||
| @ -218,11 +204,6 @@ impl crate::engine::EngineManager for EngineConnection { | ||||
|             .do_send_modeling_cmd(id, source_range, cmd, id_to_source_range) | ||||
|             .await?; | ||||
|  | ||||
|         // In isolated mode, we don't save the response. | ||||
|         if self.execution_kind().await.is_isolated() { | ||||
|             return Ok(ws_result); | ||||
|         } | ||||
|  | ||||
|         let mut responses = self.responses.write().await; | ||||
|         responses.insert(id, ws_result.clone()); | ||||
|         drop(responses); | ||||
|  | ||||
| @ -47,23 +47,6 @@ lazy_static::lazy_static! { | ||||
|     pub static ref GRID_SCALE_TEXT_OBJECT_ID: uuid::Uuid = uuid::Uuid::parse_str("10782f33-f588-4668-8bcd-040502d26590").unwrap(); | ||||
| } | ||||
|  | ||||
| /// The mode of execution.  When isolated, like during an import, attempting to | ||||
| /// send a command results in an error. | ||||
| #[derive(Debug, Default, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, ts_rs::TS, JsonSchema)] | ||||
| #[ts(export)] | ||||
| #[serde(rename_all = "camelCase")] | ||||
| pub enum ExecutionKind { | ||||
|     #[default] | ||||
|     Normal, | ||||
|     Isolated, | ||||
| } | ||||
|  | ||||
| impl ExecutionKind { | ||||
|     pub fn is_isolated(&self) -> bool { | ||||
|         matches!(self, ExecutionKind::Isolated) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Default, Debug)] | ||||
| pub struct EngineStats { | ||||
|     pub commands_batched: AtomicUsize, | ||||
| @ -108,13 +91,6 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static { | ||||
|         std::mem::take(&mut *self.responses().write().await) | ||||
|     } | ||||
|  | ||||
|     /// Get the current execution kind. | ||||
|     async fn execution_kind(&self) -> ExecutionKind; | ||||
|  | ||||
|     /// Replace the current execution kind with a new value and return the | ||||
|     /// existing value. | ||||
|     async fn replace_execution_kind(&self, execution_kind: ExecutionKind) -> ExecutionKind; | ||||
|  | ||||
|     /// Get the default planes. | ||||
|     fn get_default_planes(&self) -> Arc<RwLock<Option<DefaultPlanes>>>; | ||||
|  | ||||
| @ -279,11 +255,6 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static { | ||||
|         source_range: SourceRange, | ||||
|         cmd: &ModelingCmd, | ||||
|     ) -> Result<(), crate::errors::KclError> { | ||||
|         // In isolated mode, we don't send the command to the engine. | ||||
|         if self.execution_kind().await.is_isolated() { | ||||
|             return Ok(()); | ||||
|         } | ||||
|  | ||||
|         let req = WebSocketRequest::ModelingCmdReq(ModelingCmdReq { | ||||
|             cmd: cmd.clone(), | ||||
|             cmd_id: id.into(), | ||||
| @ -305,11 +276,6 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static { | ||||
|         source_range: SourceRange, | ||||
|         cmds: &[ModelingCmdReq], | ||||
|     ) -> Result<(), crate::errors::KclError> { | ||||
|         // In isolated mode, we don't send the command to the engine. | ||||
|         if self.execution_kind().await.is_isolated() { | ||||
|             return Ok(()); | ||||
|         } | ||||
|  | ||||
|         // Add cmds to the batch. | ||||
|         let mut extended_cmds = Vec::with_capacity(cmds.len()); | ||||
|         for cmd in cmds { | ||||
| @ -332,11 +298,6 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static { | ||||
|         source_range: SourceRange, | ||||
|         cmd: &ModelingCmd, | ||||
|     ) -> Result<(), crate::errors::KclError> { | ||||
|         // In isolated mode, we don't send the command to the engine. | ||||
|         if self.execution_kind().await.is_isolated() { | ||||
|             return Ok(()); | ||||
|         } | ||||
|  | ||||
|         let req = WebSocketRequest::ModelingCmdReq(ModelingCmdReq { | ||||
|             cmd: cmd.clone(), | ||||
|             cmd_id: id.into(), | ||||
|  | ||||
| @ -5,7 +5,6 @@ use indexmap::IndexMap; | ||||
|  | ||||
| use super::{cad_op::Group, kcl_value::TypeDef, types::PrimitiveType}; | ||||
| use crate::{ | ||||
|     engine::ExecutionKind, | ||||
|     errors::{KclError, KclErrorDetails}, | ||||
|     execution::{ | ||||
|         annotations, | ||||
| @ -62,15 +61,13 @@ impl ExecutorContext { | ||||
|                         exec_state.mod_local.explicit_length_units = true; | ||||
|                     } | ||||
|                     let new_units = exec_state.length_unit(); | ||||
|                     if !self.engine.execution_kind().await.is_isolated() { | ||||
|                         self.engine | ||||
|                             .set_units( | ||||
|                                 new_units.into(), | ||||
|                                 annotation.as_source_range(), | ||||
|                                 exec_state.id_generator(), | ||||
|                             ) | ||||
|                             .await?; | ||||
|                     } | ||||
|                     self.engine | ||||
|                         .set_units( | ||||
|                             new_units.into(), | ||||
|                             annotation.as_source_range(), | ||||
|                             exec_state.id_generator(), | ||||
|                         ) | ||||
|                         .await?; | ||||
|                 } else { | ||||
|                     exec_state.err(CompilationError::err( | ||||
|                         annotation.as_source_range(), | ||||
| @ -100,15 +97,13 @@ impl ExecutorContext { | ||||
|         &self, | ||||
|         program: &Node<Program>, | ||||
|         exec_state: &mut ExecState, | ||||
|         exec_kind: ExecutionKind, | ||||
|         preserve_mem: bool, | ||||
|         module_id: ModuleId, | ||||
|         path: &ModulePath, | ||||
|     ) -> Result<(Option<KclValue>, EnvironmentRef, Vec<String>), KclError> { | ||||
|         crate::log::log(format!("enter module {path} {} {exec_kind:?}", exec_state.stack())); | ||||
|         crate::log::log(format!("enter module {path} {}", exec_state.stack())); | ||||
|  | ||||
|         let old_units = exec_state.length_unit(); | ||||
|         let original_execution = self.engine.replace_execution_kind(exec_kind).await; | ||||
|  | ||||
|         let mut local_state = ModuleState::new(path.std_path(), exec_state.stack().memory.clone(), Some(module_id)); | ||||
|         if !preserve_mem { | ||||
| @ -141,23 +136,71 @@ impl ExecutorContext { | ||||
|         // If we reset at the end of the main path, then we just add on an extra | ||||
|         // command and we'd need to flush the batch again. | ||||
|         // This avoids that. | ||||
|         if !exec_kind.is_isolated() && new_units != old_units && *path != ModulePath::Main { | ||||
|         if new_units != old_units && *path != ModulePath::Main { | ||||
|             self.engine | ||||
|                 .set_units(old_units.into(), Default::default(), exec_state.id_generator()) | ||||
|                 .await?; | ||||
|         } | ||||
|         self.engine.replace_execution_kind(original_execution).await; | ||||
|  | ||||
|         crate::log::log(format!("leave {path}")); | ||||
|  | ||||
|         result.map(|result| (result, env_ref, local_state.module_exports)) | ||||
|     } | ||||
|  | ||||
|     /// Execute an AST's program. | ||||
|     #[async_recursion] | ||||
|     pub(super) async fn preload_all_modules<'a>( | ||||
|         &'a self, | ||||
|         modules: &mut HashMap<String, Program>, | ||||
|         program: NodeRef<'a, Program>, | ||||
|         exec_state: &mut ExecState, | ||||
|     ) -> Result<(), KclError> { | ||||
|         for statement in &program.body { | ||||
|             if let BodyItem::ImportStatement(import_stmt) = statement { | ||||
|                 let path_str = import_stmt.path.to_string(); | ||||
|  | ||||
|                 if modules.contains_key(&path_str) { | ||||
|                     // don't waste our time if we've already loaded the | ||||
|                     // module. | ||||
|                     continue; | ||||
|                 } | ||||
|  | ||||
|                 let source_range = SourceRange::from(import_stmt); | ||||
|                 let attrs = &import_stmt.outer_attrs; | ||||
|                 let module_id = self | ||||
|                     .open_module(&import_stmt.path, attrs, exec_state, source_range) | ||||
|                     .await?; | ||||
|  | ||||
|                 let Some(module) = exec_state.get_module(module_id) else { | ||||
|                     crate::log::log("we got back a module id that doesn't exist"); | ||||
|                     unreachable!(); | ||||
|                 }; | ||||
|  | ||||
|                 let progn = { | ||||
|                     // this dance is to avoid taking out a mut borrow | ||||
|                     // below on exec_state after borrowing here. As a | ||||
|                     // result, we need to clone (ugh) the program for | ||||
|                     // now. | ||||
|                     let ModuleRepr::Kcl(ref progn, _) = module.repr else { | ||||
|                         // not a kcl file, we can skip this | ||||
|                         continue; | ||||
|                     }; | ||||
|                     progn.clone() | ||||
|                 }; | ||||
|  | ||||
|                 modules.insert(path_str, progn.clone().inner); | ||||
|  | ||||
|                 self.preload_all_modules(modules, &progn, exec_state).await?; | ||||
|             }; | ||||
|         } | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     /// Execute an AST's program. | ||||
|     #[async_recursion] | ||||
|     pub(super) async fn exec_block<'a>( | ||||
|         &'a self, | ||||
|         program: NodeRef<'a, crate::parsing::ast::types::Program>, | ||||
|         program: NodeRef<'a, Program>, | ||||
|         exec_state: &mut ExecState, | ||||
|         body_type: BodyType, | ||||
|     ) -> Result<Option<KclValue>, KclError> { | ||||
| @ -181,9 +224,9 @@ impl ExecutorContext { | ||||
|  | ||||
|                     match &import_stmt.selector { | ||||
|                         ImportSelector::List { items } => { | ||||
|                             let (env_ref, module_exports) = self | ||||
|                                 .exec_module_for_items(module_id, exec_state, ExecutionKind::Isolated, source_range) | ||||
|                                 .await?; | ||||
|                             println!("Importing items from module {}", import_stmt.path,); | ||||
|                             let (env_ref, module_exports) = | ||||
|                                 self.exec_module_for_items(module_id, exec_state, source_range).await?; | ||||
|                             for import_item in items { | ||||
|                                 // Extract the item from the module. | ||||
|                                 let item = exec_state | ||||
| @ -224,9 +267,9 @@ impl ExecutorContext { | ||||
|                             } | ||||
|                         } | ||||
|                         ImportSelector::Glob(_) => { | ||||
|                             let (env_ref, module_exports) = self | ||||
|                                 .exec_module_for_items(module_id, exec_state, ExecutionKind::Isolated, source_range) | ||||
|                                 .await?; | ||||
|                             println!("Importing all items from module {}", import_stmt.path); | ||||
|                             let (env_ref, module_exports) = | ||||
|                                 self.exec_module_for_items(module_id, exec_state, source_range).await?; | ||||
|                             for name in module_exports.iter() { | ||||
|                                 let item = exec_state | ||||
|                                     .stack() | ||||
| @ -417,7 +460,7 @@ impl ExecutorContext { | ||||
|         Ok(last_expr) | ||||
|     } | ||||
|  | ||||
|     pub(super) async fn open_module( | ||||
|     pub async fn open_module( | ||||
|         &self, | ||||
|         path: &ImportPath, | ||||
|         attrs: &[Node<Annotation>], | ||||
| @ -425,6 +468,7 @@ impl ExecutorContext { | ||||
|         source_range: SourceRange, | ||||
|     ) -> Result<ModuleId, KclError> { | ||||
|         let resolved_path = ModulePath::from_import_path(path, &self.settings.project_directory); | ||||
|  | ||||
|         match path { | ||||
|             ImportPath::Kcl { .. } => { | ||||
|                 exec_state.global.mod_loader.cycle_check(&resolved_path, source_range)?; | ||||
| @ -481,7 +525,6 @@ impl ExecutorContext { | ||||
|         &self, | ||||
|         module_id: ModuleId, | ||||
|         exec_state: &mut ExecState, | ||||
|         exec_kind: ExecutionKind, | ||||
|         source_range: SourceRange, | ||||
|     ) -> Result<(EnvironmentRef, Vec<String>), KclError> { | ||||
|         let path = exec_state.global.module_infos[&module_id].path.clone(); | ||||
| @ -492,7 +535,7 @@ impl ExecutorContext { | ||||
|             ModuleRepr::Root => Err(exec_state.circular_import_error(&path, source_range)), | ||||
|             ModuleRepr::Kcl(_, Some((env_ref, items))) => Ok((*env_ref, items.clone())), | ||||
|             ModuleRepr::Kcl(program, cache) => self | ||||
|                 .exec_module_from_ast(program, module_id, &path, exec_state, exec_kind, source_range) | ||||
|                 .exec_module_from_ast(program, module_id, &path, exec_state, source_range) | ||||
|                 .await | ||||
|                 .map(|(_, er, items)| { | ||||
|                     *cache = Some((er, items.clone())); | ||||
| @ -514,7 +557,6 @@ impl ExecutorContext { | ||||
|         module_id: ModuleId, | ||||
|         module_name: &BoxNode<Name>, | ||||
|         exec_state: &mut ExecState, | ||||
|         exec_kind: ExecutionKind, | ||||
|         source_range: SourceRange, | ||||
|     ) -> Result<Option<KclValue>, KclError> { | ||||
|         exec_state.global.operations.push(Operation::GroupBegin { | ||||
| @ -533,7 +575,7 @@ impl ExecutorContext { | ||||
|             ModuleRepr::Root => Err(exec_state.circular_import_error(&path, source_range)), | ||||
|             ModuleRepr::Kcl(program, cached_items) => { | ||||
|                 let result = self | ||||
|                     .exec_module_from_ast(program, module_id, &path, exec_state, exec_kind, source_range) | ||||
|                     .exec_module_from_ast(program, module_id, &path, exec_state, source_range) | ||||
|                     .await; | ||||
|                 match result { | ||||
|                     Ok((val, env, items)) => { | ||||
| @ -556,19 +598,17 @@ impl ExecutorContext { | ||||
|         result | ||||
|     } | ||||
|  | ||||
|     async fn exec_module_from_ast( | ||||
|     pub async fn exec_module_from_ast( | ||||
|         &self, | ||||
|         program: &Node<Program>, | ||||
|         module_id: ModuleId, | ||||
|         path: &ModulePath, | ||||
|         exec_state: &mut ExecState, | ||||
|         exec_kind: ExecutionKind, | ||||
|         source_range: SourceRange, | ||||
|     ) -> Result<(Option<KclValue>, EnvironmentRef, Vec<String>), KclError> { | ||||
|         println!("exec_module_from_ast {path}"); | ||||
|         exec_state.global.mod_loader.enter_module(path); | ||||
|         let result = self | ||||
|             .exec_module_body(program, exec_state, exec_kind, false, module_id, path) | ||||
|             .await; | ||||
|         let result = self.exec_module_body(program, exec_state, false, module_id, path).await; | ||||
|         exec_state.global.mod_loader.leave_module(path); | ||||
|  | ||||
|         result.map_err(|err| { | ||||
| @ -604,7 +644,7 @@ impl ExecutorContext { | ||||
|             Expr::Name(name) => { | ||||
|                 let value = name.get_result(exec_state, self).await?.clone(); | ||||
|                 if let KclValue::Module { value: module_id, meta } = value { | ||||
|                     self.exec_module_for_result(module_id, name, exec_state, ExecutionKind::Normal, metadata.source_range) | ||||
|                     self.exec_module_for_result(module_id, name, exec_state,  metadata.source_range) | ||||
|                         .await? | ||||
|                         .unwrap_or_else(|| { | ||||
|                             exec_state.warn(CompilationError::err( | ||||
| @ -792,7 +832,7 @@ impl Node<Name> { | ||||
|             }; | ||||
|  | ||||
|             mem_spec = Some( | ||||
|                 ctx.exec_module_for_items(*module_id, exec_state, ExecutionKind::Normal, p.as_source_range()) | ||||
|                 ctx.exec_module_for_items(*module_id, exec_state, p.as_source_range()) | ||||
|                     .await?, | ||||
|             ); | ||||
|         } | ||||
| @ -1273,7 +1313,7 @@ impl Node<CallExpressionKw> { | ||||
|                     )); | ||||
|                 } | ||||
|  | ||||
|                 let op = if func.feature_tree_operation() && !ctx.is_isolated_execution().await { | ||||
|                 let op = if func.feature_tree_operation() { | ||||
|                     let op_labeled_args = args | ||||
|                         .kw_args | ||||
|                         .labeled | ||||
| @ -1359,7 +1399,7 @@ impl Node<CallExpressionKw> { | ||||
|                         e.add_source_ranges(vec![callsite]) | ||||
|                     })?; | ||||
|  | ||||
|                 if matches!(fn_src, FunctionSource::User { .. }) && !ctx.is_isolated_execution().await { | ||||
|                 if matches!(fn_src, FunctionSource::User { .. }) { | ||||
|                     // Track return operation. | ||||
|                     exec_state.global.operations.push(Operation::GroupEnd); | ||||
|                 } | ||||
| @ -1411,7 +1451,7 @@ impl Node<CallExpression> { | ||||
|                     )); | ||||
|                 } | ||||
|  | ||||
|                 let op = if func.feature_tree_operation() && !ctx.is_isolated_execution().await { | ||||
|                 let op = if func.feature_tree_operation() { | ||||
|                     let op_labeled_args = func | ||||
|                         .args(false) | ||||
|                         .iter() | ||||
| @ -1469,19 +1509,17 @@ impl Node<CallExpression> { | ||||
|                 // exec_state. | ||||
|                 let func = fn_name.get_result(exec_state, ctx).await?.clone(); | ||||
|  | ||||
|                 if !ctx.is_isolated_execution().await { | ||||
|                     // Track call operation. | ||||
|                     exec_state.global.operations.push(Operation::GroupBegin { | ||||
|                         group: Group::FunctionCall { | ||||
|                             name: Some(fn_name.to_string()), | ||||
|                             function_source_range: func.function_def_source_range().unwrap_or_default(), | ||||
|                             unlabeled_arg: None, | ||||
|                             // TODO: Add the arguments for legacy positional parameters. | ||||
|                             labeled_args: Default::default(), | ||||
|                         }, | ||||
|                         source_range: callsite, | ||||
|                     }); | ||||
|                 } | ||||
|                 // Track call operation. | ||||
|                 exec_state.global.operations.push(Operation::GroupBegin { | ||||
|                     group: Group::FunctionCall { | ||||
|                         name: Some(fn_name.to_string()), | ||||
|                         function_source_range: func.function_def_source_range().unwrap_or_default(), | ||||
|                         unlabeled_arg: None, | ||||
|                         // TODO: Add the arguments for legacy positional parameters. | ||||
|                         labeled_args: Default::default(), | ||||
|                     }, | ||||
|                     source_range: callsite, | ||||
|                 }); | ||||
|  | ||||
|                 let Some(fn_src) = func.as_fn() else { | ||||
|                     return Err(KclError::Semantic(KclErrorDetails { | ||||
| @ -1510,10 +1548,8 @@ impl Node<CallExpression> { | ||||
|                     }) | ||||
|                 })?; | ||||
|  | ||||
|                 if !ctx.is_isolated_execution().await { | ||||
|                     // Track return operation. | ||||
|                     exec_state.global.operations.push(Operation::GroupEnd); | ||||
|                 } | ||||
|                 // Track return operation. | ||||
|                 exec_state.global.operations.push(Operation::GroupEnd); | ||||
|  | ||||
|                 Ok(result) | ||||
|             } | ||||
| @ -2241,7 +2277,7 @@ impl FunctionSource { | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 let op = if props.include_in_feature_tree && !ctx.is_isolated_execution().await { | ||||
|                 let op = if props.include_in_feature_tree { | ||||
|                     let op_labeled_args = args | ||||
|                         .kw_args | ||||
|                         .labeled | ||||
| @ -2285,28 +2321,26 @@ impl FunctionSource { | ||||
|                 Ok(Some(result)) | ||||
|             } | ||||
|             FunctionSource::User { ast, memory, .. } => { | ||||
|                 if !ctx.is_isolated_execution().await { | ||||
|                     // Track call operation. | ||||
|                     let op_labeled_args = args | ||||
|                         .kw_args | ||||
|                         .labeled | ||||
|                         .iter() | ||||
|                         .map(|(k, arg)| (k.clone(), OpArg::new(OpKclValue::from(&arg.value), arg.source_range))) | ||||
|                         .collect(); | ||||
|                     exec_state.global.operations.push(Operation::GroupBegin { | ||||
|                         group: Group::FunctionCall { | ||||
|                             name: fn_name, | ||||
|                             function_source_range: ast.as_source_range(), | ||||
|                             unlabeled_arg: args | ||||
|                                 .kw_args | ||||
|                                 .unlabeled | ||||
|                                 .as_ref() | ||||
|                                 .map(|arg| OpArg::new(OpKclValue::from(&arg.value), arg.source_range)), | ||||
|                             labeled_args: op_labeled_args, | ||||
|                         }, | ||||
|                         source_range: callsite, | ||||
|                     }); | ||||
|                 } | ||||
|                 // Track call operation. | ||||
|                 let op_labeled_args = args | ||||
|                     .kw_args | ||||
|                     .labeled | ||||
|                     .iter() | ||||
|                     .map(|(k, arg)| (k.clone(), OpArg::new(OpKclValue::from(&arg.value), arg.source_range))) | ||||
|                     .collect(); | ||||
|                 exec_state.global.operations.push(Operation::GroupBegin { | ||||
|                     group: Group::FunctionCall { | ||||
|                         name: fn_name, | ||||
|                         function_source_range: ast.as_source_range(), | ||||
|                         unlabeled_arg: args | ||||
|                             .kw_args | ||||
|                             .unlabeled | ||||
|                             .as_ref() | ||||
|                             .map(|arg| OpArg::new(OpKclValue::from(&arg.value), arg.source_range)), | ||||
|                         labeled_args: op_labeled_args, | ||||
|                     }, | ||||
|                     source_range: callsite, | ||||
|                 }); | ||||
|  | ||||
|                 call_user_defined_function_kw(args.kw_args, *memory, ast, exec_state, ctx).await | ||||
|             } | ||||
| @ -2317,13 +2351,14 @@ impl FunctionSource { | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod test { | ||||
|     use std::sync::Arc; | ||||
|  | ||||
|     use super::*; | ||||
|     use crate::{ | ||||
|         execution::{memory::Stack, parse_execute, ContextType}, | ||||
|         parsing::ast::types::{DefaultParamVal, Identifier, Parameter}, | ||||
|         ExecutorSettings, | ||||
|     }; | ||||
|     use std::sync::Arc; | ||||
|     use tokio::io::AsyncWriteExt; | ||||
|  | ||||
|     #[tokio::test(flavor = "multi_thread")] | ||||
|     async fn test_assign_args_to_params() { | ||||
| @ -2432,7 +2467,7 @@ mod test { | ||||
|             // Run each test. | ||||
|             let func_expr = &Node::no_src(FunctionExpression { | ||||
|                 params, | ||||
|                 body: crate::parsing::ast::types::Program::empty(), | ||||
|                 body: Program::empty(), | ||||
|                 return_type: None, | ||||
|                 digest: None, | ||||
|             }); | ||||
| @ -2534,4 +2569,100 @@ a = foo() | ||||
|         let result = parse_execute(program).await; | ||||
|         assert!(result.unwrap_err().to_string().contains("return")); | ||||
|     } | ||||
|  | ||||
|     #[tokio::test(flavor = "multi_thread")] | ||||
|     async fn load_all_modules() { | ||||
|         // program a.kcl | ||||
|         let programa_kcl = r#" | ||||
| export a = 1 | ||||
| "#; | ||||
|         // program b.kcl | ||||
|         let programb_kcl = r#" | ||||
| import a from 'a.kcl' | ||||
|  | ||||
| export b = a + 1 | ||||
| "#; | ||||
|         // program c.kcl | ||||
|         let programc_kcl = r#" | ||||
| import a from 'a.kcl' | ||||
|  | ||||
| export c = a + 2 | ||||
| "#; | ||||
|  | ||||
|         // program main.kcl | ||||
|         let main_kcl = r#" | ||||
| import b from 'b.kcl' | ||||
| import c from 'c.kcl' | ||||
|  | ||||
| d = b + c | ||||
| "#; | ||||
|  | ||||
|         let main = crate::parsing::parse_str(main_kcl, ModuleId::default()) | ||||
|             .parse_errs_as_err() | ||||
|             .unwrap(); | ||||
|  | ||||
|         let tmpdir = tempdir::TempDir::new("zma_kcl_load_all_modules").unwrap(); | ||||
|  | ||||
|         tokio::fs::File::create(tmpdir.path().join("main.kcl")) | ||||
|             .await | ||||
|             .unwrap() | ||||
|             .write_all(main_kcl.as_bytes()) | ||||
|             .await | ||||
|             .unwrap(); | ||||
|  | ||||
|         tokio::fs::File::create(tmpdir.path().join("a.kcl")) | ||||
|             .await | ||||
|             .unwrap() | ||||
|             .write_all(programa_kcl.as_bytes()) | ||||
|             .await | ||||
|             .unwrap(); | ||||
|  | ||||
|         tokio::fs::File::create(tmpdir.path().join("b.kcl")) | ||||
|             .await | ||||
|             .unwrap() | ||||
|             .write_all(programb_kcl.as_bytes()) | ||||
|             .await | ||||
|             .unwrap(); | ||||
|  | ||||
|         tokio::fs::File::create(tmpdir.path().join("c.kcl")) | ||||
|             .await | ||||
|             .unwrap() | ||||
|             .write_all(programc_kcl.as_bytes()) | ||||
|             .await | ||||
|             .unwrap(); | ||||
|  | ||||
|         let exec_ctxt = ExecutorContext { | ||||
|             engine: Arc::new(Box::new( | ||||
|                 crate::engine::conn_mock::EngineConnection::new() | ||||
|                     .await | ||||
|                     .map_err(|err| { | ||||
|                         KclError::Internal(crate::errors::KclErrorDetails { | ||||
|                             message: format!("Failed to create mock engine connection: {}", err), | ||||
|                             source_ranges: vec![SourceRange::default()], | ||||
|                         }) | ||||
|                     }) | ||||
|                     .unwrap(), | ||||
|             )), | ||||
|             fs: Arc::new(crate::fs::FileManager::new()), | ||||
|             settings: ExecutorSettings { | ||||
|                 project_directory: Some(tmpdir.path().into()), | ||||
|                 ..Default::default() | ||||
|             }, | ||||
|             stdlib: Arc::new(crate::std::StdLib::new()), | ||||
|             context_type: ContextType::Mock, | ||||
|         }; | ||||
|         let mut exec_state = ExecState::new(&exec_ctxt); | ||||
|  | ||||
|         exec_ctxt | ||||
|             .run_concurrent( | ||||
|                 &crate::Program { | ||||
|                     ast: main.clone(), | ||||
|                     original_file_contents: "".to_owned(), | ||||
|                 }, | ||||
|                 &mut exec_state, | ||||
|                 false, | ||||
|             ) | ||||
|             .await | ||||
|             .unwrap(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -27,21 +27,22 @@ pub use memory::EnvironmentRef; | ||||
| use schemars::JsonSchema; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| pub use state::{ExecState, MetaSettings}; | ||||
| use tokio::task::JoinSet; | ||||
|  | ||||
| use crate::{ | ||||
|     engine::EngineManager, | ||||
|     errors::KclError, | ||||
|     errors::{KclError, KclErrorDetails}, | ||||
|     execution::{ | ||||
|         artifact::build_artifact_graph, | ||||
|         cache::{CacheInformation, CacheResult}, | ||||
|         types::{UnitAngle, UnitLen}, | ||||
|     }, | ||||
|     fs::FileManager, | ||||
|     modules::{ModuleId, ModulePath}, | ||||
|     modules::{ModuleId, ModulePath, ModuleRepr}, | ||||
|     parsing::ast::types::{Expr, ImportPath, NodeRef}, | ||||
|     source_range::SourceRange, | ||||
|     std::StdLib, | ||||
|     CompilationError, ExecError, ExecutionKind, KclErrorWithOutputs, | ||||
|     CompilationError, ExecError, KclErrorWithOutputs, | ||||
| }; | ||||
|  | ||||
| pub(crate) mod annotations; | ||||
| @ -497,13 +498,9 @@ impl ExecutorContext { | ||||
|         self.context_type == ContextType::Mock || self.context_type == ContextType::MockCustomForwarded | ||||
|     } | ||||
|  | ||||
|     pub async fn is_isolated_execution(&self) -> bool { | ||||
|         self.engine.execution_kind().await.is_isolated() | ||||
|     } | ||||
|  | ||||
|     /// Returns true if we should not send engine commands for any reason. | ||||
|     pub async fn no_engine_commands(&self) -> bool { | ||||
|         self.is_mock() || self.is_isolated_execution().await | ||||
|         self.is_mock() | ||||
|     } | ||||
|  | ||||
|     pub async fn send_clear_scene( | ||||
| @ -674,7 +671,7 @@ impl ExecutorContext { | ||||
|             (program, exec_state, false) | ||||
|         }; | ||||
|  | ||||
|         let result = self.inner_run(&program, &mut exec_state, preserve_mem).await; | ||||
|         let result = self.run_concurrent(&program, &mut exec_state, preserve_mem).await; | ||||
|  | ||||
|         if result.is_err() { | ||||
|             cache::bust_cache().await; | ||||
| @ -707,7 +704,124 @@ impl ExecutorContext { | ||||
|         program: &crate::Program, | ||||
|         exec_state: &mut ExecState, | ||||
|     ) -> Result<(EnvironmentRef, Option<ModelingSessionData>), KclErrorWithOutputs> { | ||||
|         self.inner_run(program, exec_state, false).await | ||||
|         self.run_concurrent(program, exec_state, false).await | ||||
|     } | ||||
|  | ||||
|     /// Perform the execution of a program using an (experimental!) concurrent | ||||
|     /// execution model. This has the same signature as [Self::run]. | ||||
|     /// | ||||
|     /// For now -- do not use this unless you're willing to accept some | ||||
|     /// breakage. | ||||
|     /// | ||||
|     /// You can optionally pass in some initialization memory for partial | ||||
|     /// execution. | ||||
|     /// | ||||
|     /// To access non-fatal errors and warnings, extract them from the `ExecState`. | ||||
|     pub async fn run_concurrent( | ||||
|         &self, | ||||
|         program: &crate::Program, | ||||
|         exec_state: &mut ExecState, | ||||
|         preserve_mem: bool, | ||||
|     ) -> Result<(EnvironmentRef, Option<ModelingSessionData>), KclErrorWithOutputs> { | ||||
|         self.prepare_mem(exec_state).await.unwrap(); | ||||
|  | ||||
|         let mut universe = std::collections::HashMap::new(); | ||||
|         let mut out_id_map = std::collections::HashMap::new(); | ||||
|  | ||||
|         crate::walk::import_universe(self, &program.ast, &mut universe, &mut out_id_map, exec_state) | ||||
|             .await | ||||
|             .unwrap(); | ||||
|  | ||||
|         for modules in crate::walk::import_graph(&universe).unwrap().into_iter() { | ||||
|             println!("Running module {modules:?}"); | ||||
|             //let mut set = JoinSet::new(); | ||||
|             println!("AFTER Running module {modules:?}"); | ||||
|             let (results_tx, mut results_rx): ( | ||||
|                 tokio::sync::mpsc::Sender<( | ||||
|                     String, | ||||
|                     Result<(Option<KclValue>, EnvironmentRef, Vec<String>), KclError>, | ||||
|                 )>, | ||||
|                 tokio::sync::mpsc::Receiver<_>, | ||||
|             ) = tokio::sync::mpsc::channel(1); | ||||
|  | ||||
|             for module in modules { | ||||
|                 let program = universe.get(&module).unwrap().clone(); | ||||
|                 let Some(module_id) = out_id_map.get(&module) else { | ||||
|                     panic!("Module {module} not found in exec_state"); | ||||
|                 }; | ||||
|                 let module_id = module_id.clone(); | ||||
|                 let module_path = { | ||||
|                     let module_info = exec_state.get_module(module_id).unwrap(); | ||||
|                     let module_path = module_info.path.clone(); | ||||
|                     module_path | ||||
|                 }; | ||||
|                 let exec_state = exec_state.clone(); | ||||
|                 let exec_ctxt = self.clone(); | ||||
|                 let results_tx = results_tx.clone(); | ||||
|  | ||||
|                 println!("before spawn"); | ||||
|                 #[cfg(target_arch = "wasm32")] | ||||
|                 { | ||||
|                     wasm_bindgen_futures::spawn_local(async move { | ||||
|                         //set.spawn(async move { | ||||
|                         println!("Running module {module} from run_concurrent"); | ||||
|                         let mut exec_state = exec_state; | ||||
|                         let exec_ctxt = exec_ctxt; | ||||
|                         let program = program; | ||||
|  | ||||
|                         let result = exec_ctxt | ||||
|                             .exec_module_from_ast( | ||||
|                                 &program, | ||||
|                                 module_id, | ||||
|                                 &module_path, | ||||
|                                 &mut exec_state, | ||||
|                                 Default::default(), | ||||
|                             ) | ||||
|                             .await; | ||||
|  | ||||
|                         results_tx.send((module, result)).await.unwrap(); | ||||
|                     }); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             drop(results_tx); | ||||
|  | ||||
|             while let Some((module, result)) = results_rx.recv().await { | ||||
|                 match result { | ||||
|                     Ok((env_ref, session_data, variables)) => { | ||||
|                         println!("{module} {:?}", variables); | ||||
|                         let Some(module_id) = out_id_map.get(&module) else { | ||||
|                             //let snapshot_png_bytes = self.prepare_snapshot().await.unwrap().contents.0; | ||||
|                             // Save to a file. | ||||
|                             //tokio::fs::write("snapshot.png", snapshot_png_bytes).await.unwrap(); | ||||
|  | ||||
|                             return Err(KclErrorWithOutputs::no_outputs(KclError::Internal(KclErrorDetails { | ||||
|                                 message: format!("Module {module} not found in exec_state"), | ||||
|                                 source_ranges: Default::default(), | ||||
|                             }))); | ||||
|                         }; | ||||
|                         let path = exec_state.global.module_infos[module_id].path.clone(); | ||||
|                         let mut repr = exec_state.global.module_infos[module_id].take_repr(); | ||||
|  | ||||
|                         let ModuleRepr::Kcl(program, cache) = &mut repr else { | ||||
|                             continue; | ||||
|                         }; | ||||
|  | ||||
|                         *cache = Some((session_data, variables.clone())); | ||||
|  | ||||
|                         exec_state.global.module_infos[module_id].restore_repr(repr); | ||||
|                     } | ||||
|                     Err(e) => { | ||||
|                         //let snapshot_png_bytes = self.prepare_snapshot().await.unwrap().contents.0; | ||||
|                         // Save to a file. | ||||
|                         //tokio::fs::write("snapshot.png", snapshot_png_bytes).await.unwrap(); | ||||
|                         return Err(KclErrorWithOutputs::no_outputs(e)); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         self.inner_run(program, exec_state, preserve_mem).await | ||||
|     } | ||||
|  | ||||
|     /// Perform the execution of a program.  Accept all possible parameters and | ||||
| @ -758,11 +872,11 @@ impl ExecutorContext { | ||||
|             ) | ||||
|         })?; | ||||
|  | ||||
|         if !self.is_mock() { | ||||
|         /*        if !self.is_mock() { | ||||
|             let mut mem = exec_state.stack().deep_clone(); | ||||
|             mem.restore_env(env_ref); | ||||
|             cache::write_old_memory((mem, exec_state.global.module_infos.clone())).await; | ||||
|         } | ||||
|         }*/ | ||||
|         let session_data = self.engine.get_session_data().await; | ||||
|         Ok((env_ref, session_data)) | ||||
|     } | ||||
| @ -785,7 +899,6 @@ impl ExecutorContext { | ||||
|             .exec_module_body( | ||||
|                 program, | ||||
|                 exec_state, | ||||
|                 ExecutionKind::Normal, | ||||
|                 preserve_mem, | ||||
|                 ModuleId::default(), | ||||
|                 &ModulePath::Main, | ||||
| @ -839,9 +952,7 @@ impl ExecutorContext { | ||||
|                     source_range, | ||||
|                 ) | ||||
|                 .await?; | ||||
|             let (module_memory, _) = self | ||||
|                 .exec_module_for_items(id, exec_state, ExecutionKind::Isolated, source_range) | ||||
|                 .await?; | ||||
|             let (module_memory, _) = self.exec_module_for_items(id, exec_state, source_range).await?; | ||||
|  | ||||
|             exec_state.mut_stack().memory.set_std(module_memory); | ||||
|         } | ||||
|  | ||||
| @ -228,6 +228,10 @@ impl ExecState { | ||||
|         self.global.module_infos.insert(id, module_info); | ||||
|     } | ||||
|  | ||||
|     pub fn get_module(&mut self, id: ModuleId) -> Option<&ModuleInfo> { | ||||
|         self.global.module_infos.get(&id) | ||||
|     } | ||||
|  | ||||
|     pub fn length_unit(&self) -> UnitLen { | ||||
|         self.mod_local.settings.default_length_units | ||||
|     } | ||||
|  | ||||
| @ -76,12 +76,12 @@ pub mod std; | ||||
| pub mod test_server; | ||||
| mod thread; | ||||
| mod unparser; | ||||
| mod walk; | ||||
| pub mod walk; | ||||
| #[cfg(target_arch = "wasm32")] | ||||
| mod wasm; | ||||
|  | ||||
| pub use coredump::CoreDump; | ||||
| pub use engine::{EngineManager, EngineStats, ExecutionKind}; | ||||
| pub use engine::{EngineManager, EngineStats}; | ||||
| pub use errors::{ | ||||
|     CompilationError, ConnectionError, ExecError, KclError, KclErrorWithOutputs, Report, ReportWithOutputs, | ||||
| }; | ||||
|  | ||||
| @ -6,8 +6,10 @@ use std::{ | ||||
| use anyhow::Result; | ||||
|  | ||||
| use crate::{ | ||||
|     parsing::ast::types::{ImportPath, NodeRef, Program}, | ||||
|     modules::ModuleRepr, | ||||
|     parsing::ast::types::{ImportPath, Node as AstNode, NodeRef, Program}, | ||||
|     walk::{Node, Visitable}, | ||||
|     ExecState, ExecutorContext, ModuleId, | ||||
| }; | ||||
|  | ||||
| /// Specific dependency between two modules. The 0th element of this tuple | ||||
| @ -23,7 +25,7 @@ type Graph = Vec<Dependency>; | ||||
| /// run concurrently. Each "stage" is blocking in this model, which will | ||||
| /// change in the future. Don't use this function widely, yet. | ||||
| #[allow(clippy::iter_over_hash_type)] | ||||
| pub fn import_graph(progs: HashMap<String, NodeRef<'_, Program>>) -> Result<Vec<Vec<String>>> { | ||||
| pub fn import_graph(progs: &HashMap<String, AstNode<Program>>) -> Result<Vec<Vec<String>>> { | ||||
|     let mut graph = Graph::new(); | ||||
|  | ||||
|     for (name, program) in progs.iter() { | ||||
| @ -41,6 +43,9 @@ pub fn import_graph(progs: HashMap<String, NodeRef<'_, Program>>) -> Result<Vec< | ||||
|  | ||||
| #[allow(clippy::iter_over_hash_type)] | ||||
| fn topsort(all_modules: &[&str], graph: Graph) -> Result<Vec<Vec<String>>> { | ||||
|     if all_modules.is_empty() { | ||||
|         return Ok(vec![]); | ||||
|     } | ||||
|     let mut dep_map = HashMap::<String, Vec<String>>::new(); | ||||
|  | ||||
|     for (dependent, dependency) in graph.iter() { | ||||
| @ -57,6 +62,7 @@ fn topsort(all_modules: &[&str], graph: Graph) -> Result<Vec<Vec<String>>> { | ||||
|     let mut order = vec![]; | ||||
|  | ||||
|     loop { | ||||
|         println!("waiting_modules: {:?}", waiting_modules); | ||||
|         // Each pass through we need to find any modules which have nothing | ||||
|         // "pointing at it" -- so-called reverse dependencies. This is an entry | ||||
|         // that is either not in the dep_map OR an empty list. | ||||
| @ -101,7 +107,7 @@ fn topsort(all_modules: &[&str], graph: Graph) -> Result<Vec<Vec<String>>> { | ||||
|     Ok(order) | ||||
| } | ||||
|  | ||||
| pub(crate) fn import_dependencies(prog: NodeRef<'_, Program>) -> Result<Vec<String>> { | ||||
| pub(crate) fn import_dependencies(prog: NodeRef<Program>) -> Result<Vec<String>> { | ||||
|     let ret = Arc::new(Mutex::new(vec![])); | ||||
|  | ||||
|     fn walk(ret: Arc<Mutex<Vec<String>>>, node: Node<'_>) { | ||||
| @ -125,6 +131,51 @@ pub(crate) fn import_dependencies(prog: NodeRef<'_, Program>) -> Result<Vec<Stri | ||||
|     Ok(ret) | ||||
| } | ||||
|  | ||||
| pub(crate) async fn import_universe<'prog>( | ||||
|     ctx: &ExecutorContext, | ||||
|     prog: NodeRef<'prog, Program>, | ||||
|     out: &mut HashMap<String, AstNode<Program>>, | ||||
|     out_id_map: &mut HashMap<String, ModuleId>, | ||||
|     exec_state: &mut ExecState, | ||||
| ) -> Result<()> { | ||||
|     let modules = import_dependencies(prog)?; | ||||
|     for module in modules { | ||||
|         eprintln!("{:?}", module); | ||||
|  | ||||
|         if out.contains_key(&module) { | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         let module_id = ctx | ||||
|             .open_module( | ||||
|                 &ImportPath::Kcl { | ||||
|                     filename: module.to_string(), | ||||
|                 }, | ||||
|                 &[], | ||||
|                 exec_state, | ||||
|                 Default::default(), | ||||
|             ) | ||||
|             .await?; | ||||
|         out_id_map.insert(module.clone(), module_id); | ||||
|  | ||||
|         let program = { | ||||
|             let Some(module_info) = exec_state.get_module(module_id) else { | ||||
|                 // We should never get here we just fucking added it. | ||||
|                 anyhow::bail!("Module {} not found", module); | ||||
|             }; | ||||
|             let ModuleRepr::Kcl(program, _) = &module_info.repr else { | ||||
|                 anyhow::bail!("Module {} is not a KCL program", module); | ||||
|             }; | ||||
|             program.clone() | ||||
|         }; | ||||
|  | ||||
|         out.insert(module.clone(), program.clone()); | ||||
|         Box::pin(import_universe(ctx, &program, out, out_id_map, exec_state)).await?; | ||||
|     } | ||||
|  | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use super::*; | ||||
| @ -140,16 +191,16 @@ mod tests { | ||||
|         let mut modules = HashMap::new(); | ||||
|  | ||||
|         let a = kcl!(""); | ||||
|         modules.insert("a.kcl".to_owned(), &a); | ||||
|         modules.insert("a.kcl".to_owned(), a); | ||||
|  | ||||
|         let b = kcl!( | ||||
|             " | ||||
| import \"a.kcl\" | ||||
| " | ||||
|         ); | ||||
|         modules.insert("b.kcl".to_owned(), &b); | ||||
|         modules.insert("b.kcl".to_owned(), b); | ||||
|  | ||||
|         let order = import_graph(modules).unwrap(); | ||||
|         let order = import_graph(&modules).unwrap(); | ||||
|         assert_eq!(vec![vec!["a.kcl".to_owned()], vec!["b.kcl".to_owned()]], order); | ||||
|     } | ||||
|  | ||||
| @ -162,16 +213,16 @@ import \"a.kcl\" | ||||
| y = 2 | ||||
| " | ||||
|         ); | ||||
|         modules.insert("a.kcl".to_owned(), &a); | ||||
|         modules.insert("a.kcl".to_owned(), a); | ||||
|  | ||||
|         let b = kcl!( | ||||
|             " | ||||
| x = 1 | ||||
| " | ||||
|         ); | ||||
|         modules.insert("b.kcl".to_owned(), &b); | ||||
|         modules.insert("b.kcl".to_owned(), b); | ||||
|  | ||||
|         let order = import_graph(modules).unwrap(); | ||||
|         let order = import_graph(&modules).unwrap(); | ||||
|         assert_eq!(vec![vec!["a.kcl".to_owned(), "b.kcl".to_owned()]], order); | ||||
|     } | ||||
|  | ||||
| @ -180,23 +231,23 @@ x = 1 | ||||
|         let mut modules = HashMap::new(); | ||||
|  | ||||
|         let a = kcl!(""); | ||||
|         modules.insert("a.kcl".to_owned(), &a); | ||||
|         modules.insert("a.kcl".to_owned(), a); | ||||
|  | ||||
|         let b = kcl!( | ||||
|             " | ||||
| import \"a.kcl\" | ||||
| " | ||||
|         ); | ||||
|         modules.insert("b.kcl".to_owned(), &b); | ||||
|         modules.insert("b.kcl".to_owned(), b); | ||||
|  | ||||
|         let c = kcl!( | ||||
|             " | ||||
| import \"a.kcl\" | ||||
| " | ||||
|         ); | ||||
|         modules.insert("c.kcl".to_owned(), &c); | ||||
|         modules.insert("c.kcl".to_owned(), c); | ||||
|  | ||||
|         let order = import_graph(modules).unwrap(); | ||||
|         let order = import_graph(&modules).unwrap(); | ||||
|         assert_eq!( | ||||
|             vec![vec!["a.kcl".to_owned()], vec!["b.kcl".to_owned(), "c.kcl".to_owned()]], | ||||
|             order | ||||
| @ -212,15 +263,15 @@ import \"a.kcl\" | ||||
| import \"b.kcl\" | ||||
| " | ||||
|         ); | ||||
|         modules.insert("a.kcl".to_owned(), &a); | ||||
|         modules.insert("a.kcl".to_owned(), a); | ||||
|  | ||||
|         let b = kcl!( | ||||
|             " | ||||
| import \"a.kcl\" | ||||
| " | ||||
|         ); | ||||
|         modules.insert("b.kcl".to_owned(), &b); | ||||
|         modules.insert("b.kcl".to_owned(), b); | ||||
|  | ||||
|         import_graph(modules).unwrap_err(); | ||||
|         import_graph(&modules).unwrap_err(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -8,3 +8,5 @@ mod import_graph; | ||||
| pub use ast_node::Node; | ||||
| pub use ast_visitor::Visitable; | ||||
| pub use ast_walk::walk; | ||||
| pub use import_graph::import_graph; | ||||
| pub(crate) use import_graph::import_universe; | ||||
|  | ||||
| @ -4,7 +4,7 @@ use anyhow::Result; | ||||
| use indexmap::IndexMap; | ||||
| use kcl_lib::{ | ||||
|     exec::{ArtifactCommand, DefaultPlanes, IdGenerator}, | ||||
|     EngineStats, ExecutionKind, KclError, | ||||
|     EngineStats, KclError, | ||||
| }; | ||||
| use kittycad_modeling_cmds::{ | ||||
|     self as kcmc, | ||||
| @ -23,7 +23,6 @@ pub struct EngineConnection { | ||||
|     batch: Arc<RwLock<Vec<(WebSocketRequest, kcl_lib::SourceRange)>>>, | ||||
|     batch_end: Arc<RwLock<IndexMap<uuid::Uuid, (WebSocketRequest, kcl_lib::SourceRange)>>>, | ||||
|     core_test: Arc<RwLock<String>>, | ||||
|     execution_kind: Arc<RwLock<ExecutionKind>>, | ||||
|     /// The default planes for the scene. | ||||
|     default_planes: Arc<RwLock<Option<DefaultPlanes>>>, | ||||
|     stats: EngineStats, | ||||
| @ -37,7 +36,6 @@ impl EngineConnection { | ||||
|             batch: Arc::new(RwLock::new(Vec::new())), | ||||
|             batch_end: Arc::new(RwLock::new(IndexMap::new())), | ||||
|             core_test: result, | ||||
|             execution_kind: Default::default(), | ||||
|             default_planes: Default::default(), | ||||
|             stats: Default::default(), | ||||
|         }) | ||||
| @ -379,18 +377,6 @@ impl kcl_lib::EngineManager for EngineConnection { | ||||
|         Arc::new(RwLock::new(Vec::new())) | ||||
|     } | ||||
|  | ||||
|     async fn execution_kind(&self) -> ExecutionKind { | ||||
|         let guard = self.execution_kind.read().await; | ||||
|         *guard | ||||
|     } | ||||
|  | ||||
|     async fn replace_execution_kind(&self, execution_kind: ExecutionKind) -> ExecutionKind { | ||||
|         let mut guard = self.execution_kind.write().await; | ||||
|         let original = *guard; | ||||
|         *guard = execution_kind; | ||||
|         original | ||||
|     } | ||||
|  | ||||
|     fn get_default_planes(&self) -> Arc<RwLock<Option<DefaultPlanes>>> { | ||||
|         self.default_planes.clone() | ||||
|     } | ||||
|  | ||||
| @ -24,7 +24,7 @@ kcl-lib = { path = "../kcl-lib" } | ||||
| kittycad = { workspace = true } | ||||
| kittycad-modeling-cmds = { workspace = true } | ||||
| serde_json = { workspace = true } | ||||
| tokio = { workspace = true, features = ["sync"] } | ||||
| tokio = { workspace = true, features = ["sync", "rt"] } | ||||
| toml = "0.8.19" | ||||
| tower-lsp = { workspace = true, features = ["runtime-agnostic"] } | ||||
| uuid = { workspace = true, features = ["v4", "js", "serde"] } | ||||
|  | ||||
| @ -77,7 +77,8 @@ impl Context { | ||||
|         let program: Program = serde_json::from_str(program_ast_json).map_err(|e| e.to_string())?; | ||||
|  | ||||
|         let ctx = self.create_executor_ctx(settings, path, false)?; | ||||
|         match ctx.run_with_caching(program).await { | ||||
|         let mut exec_state = kcl_lib::ExecState::new(&ctx); | ||||
|         match ctx.run(&program, &mut exec_state).await { | ||||
|             // The serde-wasm-bindgen does not work here because of weird HashMap issues. | ||||
|             // DO NOT USE serde_wasm_bindgen::to_value it will break the frontend. | ||||
|             Ok(outcome) => JsValue::from_serde(&outcome).map_err(|e| e.to_string()), | ||||
|  | ||||
| @ -1965,6 +1965,7 @@ export class EngineCommandManager extends EventTarget { | ||||
|       range, | ||||
|       idToRangeMap, | ||||
|     }) | ||||
|       console.log("responose to wasm", resp) | ||||
|     return BSON.serialize(resp[0]) | ||||
|   } | ||||
|   /** | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	