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

@ -8,11 +8,11 @@ use crate::{
execution::{
annotations,
cad_op::{OpArg, OpKclValue, Operation},
kcl_value::{FunctionSource, NumericType, PrimitiveType, RuntimeType},
kcl_value::{FunctionSource, NumericType, RuntimeType},
memory,
state::ModuleState,
BodyType, EnvironmentRef, ExecState, ExecutorContext, KclValue, Metadata, Plane, PlaneType, Point3d,
TagEngineInfo, TagIdentifier,
BodyType, EnvironmentRef, ExecState, ExecutorContext, KclValue, Metadata, PlaneType, TagEngineInfo,
TagIdentifier,
},
modules::{ModuleId, ModulePath, ModuleRepr},
parsing::ast::types::{
@ -23,7 +23,7 @@ use crate::{
},
source_range::SourceRange,
std::{
args::{Arg, FromKclValue, KwArgs},
args::{Arg, KwArgs},
FunctionKind,
},
CompilationError,
@ -55,10 +55,9 @@ impl ExecutorContext {
for annotation in annotations {
if annotation.name() == Some(annotations::SETTINGS) {
if matches!(body_type, BodyType::Root) {
let old_units = exec_state.length_unit();
exec_state.mod_local.settings.update_from_annotation(annotation)?;
let new_units = exec_state.length_unit();
if !self.engine.execution_kind().await.is_isolated() && old_units != new_units {
if !self.engine.execution_kind().await.is_isolated() {
self.engine
.set_units(new_units.into(), annotation.as_source_range())
.await?;
@ -94,6 +93,7 @@ impl ExecutorContext {
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_state.stack()));
@ -101,7 +101,12 @@ impl ExecutorContext {
let old_units = exec_state.length_unit();
let original_execution = self.engine.replace_execution_kind(exec_kind).await;
let mut local_state = ModuleState::new(&self.settings, path.std_path(), exec_state.stack().memory.clone());
let mut local_state = ModuleState::new(
&self.settings,
path.std_path(),
exec_state.stack().memory.clone(),
Some(module_id),
);
if !preserve_mem {
std::mem::swap(&mut exec_state.mod_local, &mut local_state);
}
@ -504,7 +509,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, &path, exec_state, exec_kind, source_range)
.exec_module_from_ast(program, module_id, &path, exec_state, exec_kind, source_range)
.await
.map(|(_, er, items)| {
*cache = Some((er, items.clone()));
@ -535,7 +540,7 @@ impl ExecutorContext {
let result = match &repr {
ModuleRepr::Root => Err(exec_state.circular_import_error(&path, source_range)),
ModuleRepr::Kcl(program, _) => self
.exec_module_from_ast(program, &path, exec_state, exec_kind, source_range)
.exec_module_from_ast(program, module_id, &path, exec_state, exec_kind, source_range)
.await
.map(|(val, _, _)| val),
ModuleRepr::Foreign(geom) => super::import::send_to_engine(geom.clone(), self)
@ -551,13 +556,16 @@ impl ExecutorContext {
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> {
exec_state.global.mod_loader.enter_module(path);
let result = self.exec_module_body(program, exec_state, exec_kind, false, path).await;
let result = self
.exec_module_body(program, exec_state, exec_kind, false, module_id, path)
.await;
exec_state.global.mod_loader.leave_module(path);
result.map_err(|err| {
@ -690,11 +698,11 @@ impl ExecutorContext {
let result = self
.execute_expr(&expr.expr, exec_state, metadata, &[], statement_kind)
.await?;
coerce(result, &expr.ty, exec_state).map_err(|value| {
coerce(&result, &expr.ty, exec_state).ok_or_else(|| {
KclError::Semantic(KclErrorDetails {
message: format!(
"could not coerce {} value to type {}",
value.human_friendly_type(),
result.human_friendly_type(),
expr.ty
),
source_ranges: vec![expr.into()],
@ -706,72 +714,14 @@ impl ExecutorContext {
}
}
fn coerce(value: KclValue, ty: &Node<Type>, exec_state: &mut ExecState) -> Result<KclValue, KclValue> {
let ty = RuntimeType::from_parsed(ty.inner.clone(), exec_state, (&value).into())
fn coerce(value: &KclValue, ty: &Node<Type>, exec_state: &mut ExecState) -> Option<KclValue> {
let ty = RuntimeType::from_parsed(ty.inner.clone(), exec_state, value.into())
.map_err(|e| {
exec_state.err(e);
value.clone()
})?
.ok_or_else(|| value.clone())?;
if value.has_type(&ty) {
return Ok(value);
}
})
.ok()??;
// TODO coerce numeric types
if let KclValue::Object { value, meta } = value {
return match ty {
RuntimeType::Primitive(PrimitiveType::Plane) => {
let origin = value
.get("origin")
.and_then(Point3d::from_kcl_val)
.ok_or_else(|| KclValue::Object {
value: value.clone(),
meta: meta.clone(),
})?;
let x_axis = value
.get("xAxis")
.and_then(Point3d::from_kcl_val)
.ok_or_else(|| KclValue::Object {
value: value.clone(),
meta: meta.clone(),
})?;
let y_axis = value
.get("yAxis")
.and_then(Point3d::from_kcl_val)
.ok_or_else(|| KclValue::Object {
value: value.clone(),
meta: meta.clone(),
})?;
let z_axis = value
.get("zAxis")
.and_then(Point3d::from_kcl_val)
.ok_or_else(|| KclValue::Object {
value: value.clone(),
meta: meta.clone(),
})?;
let id = exec_state.global.id_generator.next_uuid();
let plane = Plane {
id,
artifact_id: id.into(),
origin,
x_axis,
y_axis,
z_axis,
value: PlaneType::Uninit,
// TODO use length unit from origin
units: exec_state.length_unit(),
meta,
};
Ok(KclValue::Plane { value: Box::new(plane) })
}
_ => Err(KclValue::Object { value, meta }),
};
}
Err(value)
value.coerce(&ty, exec_state)
}
impl BinaryPart {
@ -797,33 +747,7 @@ impl BinaryPart {
}
impl Node<MemberExpression> {
pub fn get_result_array(&self, exec_state: &mut ExecState, index: usize) -> Result<KclValue, KclError> {
let array = match &self.object {
MemberObject::MemberExpression(member_expr) => member_expr.get_result(exec_state)?,
MemberObject::Identifier(identifier) => {
let value = exec_state.stack().get(&identifier.name, identifier.into())?;
value.clone()
}
};
let KclValue::MixedArray { value: array, meta: _ } = array else {
return Err(KclError::Semantic(KclErrorDetails {
message: format!("MemberExpression array is not an array: {:?}", array),
source_ranges: vec![self.clone().into()],
}));
};
if let Some(value) = array.get(index) {
Ok(value.to_owned())
} else {
Err(KclError::UndefinedValue(KclErrorDetails {
message: format!("index {} not found in array", index),
source_ranges: vec![self.clone().into()],
}))
}
}
pub fn get_result(&self, exec_state: &mut ExecState) -> Result<KclValue, KclError> {
fn get_result(&self, exec_state: &mut ExecState) -> Result<KclValue, KclError> {
let property = Property::try_from(self.computed, self.property.clone(), exec_state, self.into())?;
let object = match &self.object {
// TODO: Don't use recursion here, use a loop.
@ -1424,11 +1348,22 @@ fn update_memory_for_tags_of_geometry(result: &mut KclValue, exec_state: &mut Ex
// TODO: This could probably be done in a better way, but as of now this was my only idea
// and it works.
match result {
KclValue::Sketch { value: ref mut sketch } => {
for (_, tag) in sketch.tags.iter() {
exec_state
.mut_stack()
.insert_or_update(tag.value.clone(), KclValue::TagIdentifier(Box::new(tag.clone())));
KclValue::Sketch { value } => {
for (name, tag) in value.tags.iter() {
if exec_state.stack().cur_frame_contains(name) {
exec_state.mut_stack().update(name, |v, _| {
v.as_mut_tag().unwrap().merge_info(tag);
});
} else {
exec_state
.mut_stack()
.add(
name.to_owned(),
KclValue::TagIdentifier(Box::new(tag.clone())),
SourceRange::default(),
)
.unwrap();
}
}
}
KclValue::Solid { ref mut value } => {
@ -1437,7 +1372,7 @@ fn update_memory_for_tags_of_geometry(result: &mut KclValue, exec_state: &mut Ex
// Get the past tag and update it.
let tag_id = if let Some(t) = value.sketch.tags.get(&tag.name) {
let mut t = t.clone();
let Some(ref info) = t.info else {
let Some(info) = t.get_cur_info() else {
return Err(KclError::Internal(KclErrorDetails {
message: format!("Tag {} does not have path info", tag.name),
source_ranges: vec![tag.into()],
@ -1447,59 +1382,70 @@ fn update_memory_for_tags_of_geometry(result: &mut KclValue, exec_state: &mut Ex
let mut info = info.clone();
info.surface = Some(v.clone());
info.sketch = value.id;
t.info = Some(info);
t.info.push((exec_state.stack().current_epoch(), info));
t
} else {
// It's probably a fillet or a chamfer.
// Initialize it.
TagIdentifier {
value: tag.name.clone(),
info: Some(TagEngineInfo {
id: v.get_id(),
surface: Some(v.clone()),
path: None,
sketch: value.id,
}),
info: vec![(
exec_state.stack().current_epoch(),
TagEngineInfo {
id: v.get_id(),
surface: Some(v.clone()),
path: None,
sketch: value.id,
},
)],
meta: vec![Metadata {
source_range: tag.clone().into(),
}],
}
};
exec_state
.mut_stack()
.insert_or_update(tag.name.clone(), KclValue::TagIdentifier(Box::new(tag_id.clone())));
// update the sketch tags.
value.sketch.tags.insert(tag.name.clone(), tag_id);
value.sketch.merge_tags(Some(&tag_id).into_iter());
if exec_state.stack().cur_frame_contains(&tag.name) {
exec_state.mut_stack().update(&tag.name, |v, _| {
v.as_mut_tag().unwrap().merge_info(&tag_id);
});
} else {
exec_state
.mut_stack()
.add(
tag.name.clone(),
KclValue::TagIdentifier(Box::new(tag_id)),
SourceRange::default(),
)
.unwrap();
}
}
}
// Find the stale sketch in memory and update it.
if !value.sketch.tags.is_empty() {
let updates: Vec<_> = exec_state
let sketches_to_update: Vec<_> = exec_state
.stack()
.find_all_in_current_env(|v| match v {
.find_keys_in_current_env(|v| match v {
KclValue::Sketch { value: sk } => sk.artifact_id == value.sketch.artifact_id,
_ => false,
})
.map(|(k, v)| {
let mut sketch = v.as_sketch().unwrap().clone();
for (tag_name, tag_id) in value.sketch.tags.iter() {
sketch.tags.insert(tag_name.clone(), tag_id.clone());
}
(
k.clone(),
KclValue::Sketch {
value: Box::new(sketch),
},
)
})
.cloned()
.collect();
updates
.into_iter()
.for_each(|(k, v)| exec_state.mut_stack().insert_or_update(k, v))
for k in sketches_to_update {
exec_state.mut_stack().update(&k, |v, _| {
let sketch = v.as_mut_sketch().unwrap();
sketch.merge_tags(value.sketch.tags.values());
});
}
}
}
KclValue::MixedArray { value, .. } | KclValue::HomArray { value, .. } => {
for v in value {
update_memory_for_tags_of_geometry(v, exec_state)?;
}
}
_ => {}
@ -1511,7 +1457,7 @@ impl Node<TagDeclarator> {
pub async fn execute(&self, exec_state: &mut ExecState) -> Result<KclValue, KclError> {
let memory_item = KclValue::TagIdentifier(Box::new(TagIdentifier {
value: self.name.clone(),
info: None,
info: Vec::new(),
meta: vec![Metadata {
source_range: self.into(),
}],
@ -2026,8 +1972,8 @@ mod test {
use std::sync::Arc;
use tokio::{sync::RwLock, task::JoinSet};
#[test]
fn test_assign_args_to_params() {
#[tokio::test(flavor = "multi_thread")]
async fn test_assign_args_to_params() {
// Set up a little framework for this test.
fn mem(number: usize) -> KclValue {
KclValue::Number {
@ -2138,7 +2084,16 @@ mod test {
digest: None,
});
let args = args.into_iter().map(Arg::synthetic).collect();
let mut exec_state = ExecState::new(&Default::default());
let exec_ctxt = ExecutorContext {
engine: Arc::new(Box::new(
crate::engine::conn_mock::EngineConnection::new().await.unwrap(),
)),
fs: Arc::new(crate::fs::FileManager::new()),
stdlib: Arc::new(RwLock::new(crate::std::StdLib::new())),
settings: Default::default(),
context_type: ContextType::Mock,
};
let mut exec_state = ExecState::new(&exec_ctxt);
exec_state.mod_local.stack = Stack::new_for_tests();
let actual = assign_args_to_params(func_expr, args, &mut exec_state).map(|_| exec_state.mod_local.stack);
assert_eq!(
@ -2278,7 +2233,7 @@ import 'a.kcl'
settings: Default::default(),
context_type: ContextType::Mock,
};
let mut exec_state = ExecState::new(&exec_ctxt.settings);
let mut exec_state = ExecState::new(&exec_ctxt);
eprintln!("{:?}", exec_ctxt);