Merge remote-tracking branch 'origin' into paultag/import

This commit is contained in:
Paul Tagliamonte
2025-03-18 09:17:51 -04:00
715 changed files with 457142 additions and 534601 deletions

View File

@ -10,6 +10,7 @@ use cache::OldAstState;
pub use cache::{bust_cache, clear_mem_cache};
pub use cad_op::Operation;
pub use geometry::*;
pub use id_generator::IdGenerator;
pub(crate) use import::{
import_foreign, send_to_engine as send_import_to_engine, PreImportedGeometry, ZOO_COORD_SYSTEM,
};
@ -25,7 +26,7 @@ use kittycad_modeling_cmds as kcmc;
pub use memory::EnvironmentRef;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
pub use state::{ExecState, IdGenerator, MetaSettings};
pub use state::{ExecState, MetaSettings};
use tokio::sync::RwLock;
use crate::{
@ -50,6 +51,7 @@ pub(crate) mod cache;
mod cad_op;
mod exec_ast;
mod geometry;
mod id_generator;
mod import;
pub(crate) mod kcl_value;
mod memory;
@ -73,6 +75,8 @@ pub struct ExecOutcome {
pub errors: Vec<CompilationError>,
/// File Names in module Id array index order
pub filenames: IndexMap<ModuleId, ModulePath>,
/// The default planes.
pub default_planes: Option<DefaultPlanes>,
}
#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
@ -92,11 +96,46 @@ pub struct DefaultPlanes {
#[serde(tag = "type", rename_all = "camelCase")]
pub struct TagIdentifier {
pub value: String,
pub info: Option<TagEngineInfo>,
#[serde(rename = "__meta")]
// Multi-version representation of info about the tag. Kept ordered. The usize is the epoch at which the info
// was written. Note that there might be multiple versions of tag info from the same epoch, the version with
// the higher index will be the most recent.
#[serde(skip)]
pub info: Vec<(usize, TagEngineInfo)>,
#[serde(skip)]
pub meta: Vec<Metadata>,
}
impl TagIdentifier {
/// Get the tag info for this tag at a specified epoch.
pub fn get_info(&self, at_epoch: usize) -> Option<&TagEngineInfo> {
for (e, info) in self.info.iter().rev() {
if *e <= at_epoch {
return Some(info);
}
}
None
}
/// Get the most recent tag info for this tag.
pub fn get_cur_info(&self) -> Option<&TagEngineInfo> {
self.info.last().map(|i| &i.1)
}
/// Add info from a different instance of this tag.
pub fn merge_info(&mut self, other: &TagIdentifier) {
assert_eq!(&self.value, &other.value);
'new_info: for (oe, ot) in &other.info {
for (e, _) in &self.info {
if e > oe {
continue 'new_info;
}
}
self.info.push((*oe, ot.clone()));
}
}
}
impl Eq for TagIdentifier {}
impl std::fmt::Display for TagIdentifier {
@ -111,7 +150,7 @@ impl std::str::FromStr for TagIdentifier {
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self {
value: s.to_string(),
info: None,
info: Vec::new(),
meta: Default::default(),
})
}
@ -500,7 +539,7 @@ impl ExecutorContext {
source_range: crate::execution::SourceRange,
) -> Result<(), KclError> {
self.engine
.clear_scene(&mut exec_state.global.id_generator, source_range)
.clear_scene(&mut exec_state.mod_local.id_generator, source_range)
.await
}
@ -519,7 +558,7 @@ impl ExecutorContext {
) -> Result<ExecOutcome, KclErrorWithOutputs> {
assert!(self.is_mock());
let mut exec_state = ExecState::new(&self.settings);
let mut exec_state = ExecState::new(self);
if use_prev_memory {
match cache::read_old_memory().await {
Some(mem) => *exec_state.mut_stack() = mem,
@ -540,7 +579,7 @@ impl ExecutorContext {
// memory, not to the exec_state which is not cached for mock execution.
let mut mem = exec_state.stack().clone();
let outcome = exec_state.to_mock_wasm_outcome(result.0);
let outcome = exec_state.to_mock_wasm_outcome(result.0).await;
mem.squash_env(result.0);
cache::write_old_memory(mem).await;
@ -608,13 +647,13 @@ impl ExecutorContext {
})
.await;
let outcome = old_state.to_wasm_outcome(result_env);
let outcome = old_state.to_wasm_outcome(result_env).await;
return Ok(outcome);
}
(true, program)
}
CacheResult::NoAction(false) => {
let outcome = old_state.to_wasm_outcome(result_env);
let outcome = old_state.to_wasm_outcome(result_env).await;
return Ok(outcome);
}
};
@ -622,7 +661,7 @@ impl ExecutorContext {
let (exec_state, preserve_mem) = if clear_scene {
// Pop the execution state, since we are starting fresh.
let mut exec_state = old_state;
exec_state.reset(&self.settings);
exec_state.reset(self);
// We don't do this in mock mode since there is no engine connection
// anyways and from the TS side we override memory and don't want to clear it.
@ -639,7 +678,7 @@ impl ExecutorContext {
(program, exec_state, preserve_mem)
} else {
let mut exec_state = ExecState::new(&self.settings);
let mut exec_state = ExecState::new(self);
self.send_clear_scene(&mut exec_state, Default::default())
.await
.map_err(KclErrorWithOutputs::no_outputs)?;
@ -664,7 +703,7 @@ impl ExecutorContext {
})
.await;
let outcome = exec_state.to_wasm_outcome(result.0);
let outcome = exec_state.to_wasm_outcome(result.0).await;
Ok(outcome)
}
@ -700,6 +739,7 @@ impl ExecutorContext {
.await
.map_err(KclErrorWithOutputs::no_outputs)?;
let default_planes = self.engine.get_default_planes().read().await.clone();
let env_ref = self
.execute_and_build_graph(&program.ast, exec_state, preserve_mem)
.await
@ -718,6 +758,7 @@ impl ExecutorContext {
exec_state.global.artifact_graph.clone(),
module_id_to_module_path,
exec_state.global.id_to_source.clone(),
default_planes,
)
})?;
@ -725,6 +766,7 @@ impl ExecutorContext {
"Post interpretation KCL memory stats: {:#?}",
exec_state.stack().memory.stats
));
crate::log::log(format!("Engine stats: {:?}", self.engine.stats()));
if !self.is_mock() {
let mut mem = exec_state.stack().deep_clone();
@ -755,6 +797,7 @@ impl ExecutorContext {
exec_state,
ExecutionKind::Normal,
preserve_mem,
ModuleId::default(),
&ModulePath::Main,
)
.await;
@ -890,38 +933,24 @@ impl ExecutorContext {
&self,
deterministic_time: bool,
) -> Result<Vec<kittycad_modeling_cmds::websocket::RawFile>, KclError> {
let mut files = self
let files = self
.export(kittycad_modeling_cmds::format::OutputFormat3d::Step(
kittycad_modeling_cmds::format::step::export::Options {
coords: *kittycad_modeling_cmds::coord::KITTYCAD,
created: None,
created: if deterministic_time {
Some("2021-01-01T00:00:00Z".parse().map_err(|e| {
KclError::Internal(crate::errors::KclErrorDetails {
message: format!("Failed to parse date: {}", e),
source_ranges: vec![SourceRange::default()],
})
})?)
} else {
None
},
},
))
.await?;
if deterministic_time {
for kittycad_modeling_cmds::websocket::RawFile { contents, .. } in &mut files {
use std::fmt::Write;
let utf8 = std::str::from_utf8(contents).unwrap();
let mut postprocessed = String::new();
for line in utf8.lines() {
if line.starts_with("FILE_NAME") {
let name = "test.step";
let time = "2021-01-01T00:00:00Z";
let author = "Test";
let org = "Zoo";
let version = "zoo.dev beta";
let system = "zoo.dev";
let authorization = "Test";
writeln!(&mut postprocessed, "FILE_NAME('{name}', '{time}', ('{author}'), ('{org}'), '{version}', '{system}', '{authorization}');").unwrap();
} else {
writeln!(&mut postprocessed, "{line}").unwrap();
}
}
*contents = postprocessed.into_bytes();
}
}
Ok(files)
}
@ -948,7 +977,7 @@ pub(crate) async fn parse_execute(code: &str) -> Result<ExecTestResults, KclErro
settings: Default::default(),
context_type: ContextType::Mock,
};
let mut exec_state = ExecState::new(&exec_ctxt.settings);
let mut exec_state = ExecState::new(&exec_ctxt);
let result = exec_ctxt.run(&program, &mut exec_state).await?;
Ok(ExecTestResults {
@ -978,11 +1007,7 @@ mod tests {
/// Convenience function to get a JSON value from memory and unwrap.
#[track_caller]
fn mem_get_json(memory: &Stack, env: EnvironmentRef, name: &str) -> KclValue {
memory
.memory
.get_from_unchecked(name, env, SourceRange::default())
.unwrap()
.to_owned()
memory.memory.get_from_unchecked(name, env).unwrap().to_owned()
}
#[tokio::test(flavor = "multi_thread")]
@ -1864,15 +1889,6 @@ let w = f() + f()
parse_execute(ast).await.unwrap();
}
#[test]
fn test_serialize_memory_item() {
let mem = KclValue::Solids {
value: Default::default(),
};
let json = serde_json::to_string(&mem).unwrap();
assert_eq!(json, r#"{"type":"Solids","value":[]}"#);
}
#[tokio::test(flavor = "multi_thread")]
async fn kcl_test_ids_stable_between_executions() {
let code = r#"sketch001 = startSketchOn(XZ)
@ -1895,10 +1911,14 @@ let w = f() + f()
let old_program = crate::Program::parse_no_errs(code).unwrap();
// Execute the program.
ctx.run_with_caching(old_program).await.unwrap();
if let Err(err) = ctx.run_with_caching(old_program).await {
let report = err.into_miette_report_with_outputs(code).unwrap();
let report = miette::Report::new(report);
panic!("Error executing program: {:?}", report);
}
// Get the id_generator from the first execution.
let id_generator = cache::read_old_ast().await.unwrap().exec_state.global.id_generator;
let id_generator = cache::read_old_ast().await.unwrap().exec_state.mod_local.id_generator;
let code = r#"sketch001 = startSketchOn(XZ)
|> startProfileAt([62.74, 206.13], %)
@ -1919,7 +1939,7 @@ let w = f() + f()
// Execute the program.
ctx.run_with_caching(program).await.unwrap();
let new_id_generator = cache::read_old_ast().await.unwrap().exec_state.global.id_generator;
let new_id_generator = cache::read_old_ast().await.unwrap().exec_state.mod_local.id_generator;
assert_eq!(id_generator, new_id_generator);
}
@ -1948,7 +1968,6 @@ let w = f() + f()
// Execute the program.
ctx.run_with_caching(old_program.clone()).await.unwrap();
// Get the id_generator from the first execution.
let settings_state = cache::read_old_ast().await.unwrap().settings;
// Ensure the settings are as expected.
@ -1960,7 +1979,6 @@ let w = f() + f()
// Execute the program.
ctx.run_with_caching(old_program.clone()).await.unwrap();
// Get the id_generator from the first execution.
let settings_state = cache::read_old_ast().await.unwrap().settings;
// Ensure the settings are as expected.
@ -1972,7 +1990,6 @@ let w = f() + f()
// Execute the program.
ctx.run_with_caching(old_program).await.unwrap();
// Get the id_generator from the first execution.
let settings_state = cache::read_old_ast().await.unwrap().settings;
// Ensure the settings are as expected.
@ -1991,4 +2008,41 @@ let w = f() + f()
let result = ctx2.run_mock(program2, true).await.unwrap();
assert_eq!(result.variables.get("z").unwrap().as_f64().unwrap(), 3.0);
}
#[tokio::test(flavor = "multi_thread")]
async fn read_tag_version() {
let ast = r#"fn bar(t) {
return startSketchOn(XY)
|> startProfileAt([0,0], %)
|> angledLine({
angle = -60,
length = segLen(t),
}, %)
|> line(end = [0, 0])
|> close()
}
sketch = startSketchOn(XY)
|> startProfileAt([0,0], %)
|> line(end = [0, 10])
|> line(end = [10, 0], tag = $tag0)
|> line(end = [0, 0])
fn foo() {
// tag0 tags an edge
return bar(tag0)
}
solid = sketch |> extrude(length = 10)
// tag0 tags a face
sketch2 = startSketchOn(solid, tag0)
|> startProfileAt([0,0], %)
|> line(end = [0, 1])
|> line(end = [1, 0])
|> line(end = [0, 0])
foo() |> extrude(length = 1)
"#;
parse_execute(ast).await.unwrap();
}
}