merge main
This commit is contained in:
@ -226,10 +226,7 @@ impl From<&KclValue> for OpKclValue {
|
||||
match value {
|
||||
KclValue::Uuid { value, .. } => Self::Uuid { value: *value },
|
||||
KclValue::Bool { value, .. } => Self::Bool { value: *value },
|
||||
KclValue::Number { value, ty, .. } => Self::Number {
|
||||
value: *value,
|
||||
ty: ty.clone(),
|
||||
},
|
||||
KclValue::Number { value, ty, .. } => Self::Number { value: *value, ty: *ty },
|
||||
KclValue::String { value, .. } => Self::String { value: value.clone() },
|
||||
KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } => {
|
||||
let value = value.iter().map(Self::from).collect();
|
||||
|
@ -864,6 +864,9 @@ impl BinaryPart {
|
||||
BinaryPart::CallExpressionKw(call_expression) => call_expression.execute(exec_state, ctx).await,
|
||||
BinaryPart::UnaryExpression(unary_expression) => unary_expression.get_result(exec_state, ctx).await,
|
||||
BinaryPart::MemberExpression(member_expression) => member_expression.get_result(exec_state, ctx).await,
|
||||
BinaryPart::ArrayExpression(e) => e.execute(exec_state, ctx).await,
|
||||
BinaryPart::ArrayRangeExpression(e) => e.execute(exec_state, ctx).await,
|
||||
BinaryPart::ObjectExpression(e) => e.execute(exec_state, ctx).await,
|
||||
BinaryPart::IfExpression(e) => e.get_result(exec_state, ctx).await,
|
||||
BinaryPart::AscribedExpression(e) => e.get_result(exec_state, ctx).await,
|
||||
}
|
||||
@ -1046,6 +1049,16 @@ impl Node<MemberExpression> {
|
||||
(KclValue::Solid { value }, Property::String(prop), false) if prop == "sketch" => Ok(KclValue::Sketch {
|
||||
value: Box::new(value.sketch),
|
||||
}),
|
||||
(geometry @ KclValue::Solid { .. }, Property::String(prop), false) if prop == "tags" => {
|
||||
// This is a common mistake.
|
||||
Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!(
|
||||
"Property `{prop}` not found on {}. You can get a solid's tags through its sketch, as in, `exampleSolid.sketch.tags`.",
|
||||
geometry.human_friendly_type()
|
||||
),
|
||||
vec![self.clone().into()],
|
||||
)))
|
||||
}
|
||||
(KclValue::Sketch { value: sk }, Property::String(prop), false) if prop == "tags" => Ok(KclValue::Object {
|
||||
meta: vec![Metadata {
|
||||
source_range: SourceRange::from(self.clone()),
|
||||
@ -1056,6 +1069,12 @@ impl Node<MemberExpression> {
|
||||
.map(|(k, tag)| (k.to_owned(), KclValue::TagIdentifier(Box::new(tag.to_owned()))))
|
||||
.collect(),
|
||||
}),
|
||||
(geometry @ (KclValue::Sketch { .. } | KclValue::Solid { .. }), Property::String(property), false) => {
|
||||
Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!("Property `{property}` not found on {}", geometry.human_friendly_type()),
|
||||
vec![self.clone().into()],
|
||||
)))
|
||||
}
|
||||
(being_indexed, _, _) => Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!(
|
||||
"Only arrays can be indexed, but you're trying to index {}",
|
||||
@ -1297,7 +1316,7 @@ impl Node<UnaryExpression> {
|
||||
Ok(KclValue::Number {
|
||||
value: -value,
|
||||
meta,
|
||||
ty: ty.clone(),
|
||||
ty: *ty,
|
||||
})
|
||||
}
|
||||
KclValue::Plane { value } => {
|
||||
@ -1329,7 +1348,7 @@ impl Node<UnaryExpression> {
|
||||
.map(|v| match v {
|
||||
KclValue::Number { value, ty, meta } => Ok(KclValue::Number {
|
||||
value: *value * -1.0,
|
||||
ty: ty.clone(),
|
||||
ty: *ty,
|
||||
meta: meta.clone(),
|
||||
}),
|
||||
_ => Err(err()),
|
||||
@ -1350,7 +1369,7 @@ impl Node<UnaryExpression> {
|
||||
.map(|v| match v {
|
||||
KclValue::Number { value, ty, meta } => Ok(KclValue::Number {
|
||||
value: *value * -1.0,
|
||||
ty: ty.clone(),
|
||||
ty: *ty,
|
||||
meta: meta.clone(),
|
||||
}),
|
||||
_ => Err(err()),
|
||||
@ -1544,7 +1563,7 @@ impl Node<ArrayRangeExpression> {
|
||||
.into_iter()
|
||||
.map(|num| KclValue::Number {
|
||||
value: num as f64,
|
||||
ty: start_ty.clone(),
|
||||
ty: start_ty,
|
||||
meta: meta.clone(),
|
||||
})
|
||||
.collect(),
|
||||
|
@ -401,7 +401,7 @@ impl FunctionDefinition<'_> {
|
||||
impl FunctionBody<'_> {
|
||||
fn prep_mem(&self, exec_state: &mut ExecState) {
|
||||
match self {
|
||||
FunctionBody::Rust(_) => exec_state.mut_stack().push_new_env_for_rust_call(),
|
||||
FunctionBody::Rust(_) => exec_state.mut_stack().push_new_root_env(true),
|
||||
FunctionBody::Kcl(_, memory) => exec_state.mut_stack().push_new_env_for_call(*memory),
|
||||
}
|
||||
}
|
||||
|
@ -958,6 +958,7 @@ impl From<Point3d> for Point3D {
|
||||
Self { x: p.x, y: p.y, z: p.z }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Point3d> for kittycad_modeling_cmds::shared::Point3d<LengthUnit> {
|
||||
fn from(p: Point3d) -> Self {
|
||||
Self {
|
||||
@ -1023,12 +1024,12 @@ pub struct BasePath {
|
||||
impl BasePath {
|
||||
pub fn get_to(&self) -> [TyF64; 2] {
|
||||
let ty: NumericType = self.units.into();
|
||||
[TyF64::new(self.to[0], ty.clone()), TyF64::new(self.to[1], ty)]
|
||||
[TyF64::new(self.to[0], ty), TyF64::new(self.to[1], ty)]
|
||||
}
|
||||
|
||||
pub fn get_from(&self) -> [TyF64; 2] {
|
||||
let ty: NumericType = self.units.into();
|
||||
[TyF64::new(self.from[0], ty.clone()), TyF64::new(self.from[1], ty)]
|
||||
[TyF64::new(self.from[0], ty), TyF64::new(self.from[1], ty)]
|
||||
}
|
||||
}
|
||||
|
||||
@ -1049,7 +1050,7 @@ pub struct GeoMeta {
|
||||
#[ts(export)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum Path {
|
||||
/// A path that goes to a point.
|
||||
/// A straight line which ends at the given point.
|
||||
ToPoint {
|
||||
#[serde(flatten)]
|
||||
base: BasePath,
|
||||
@ -1269,14 +1270,14 @@ impl Path {
|
||||
pub fn get_from(&self) -> [TyF64; 2] {
|
||||
let p = &self.get_base().from;
|
||||
let ty: NumericType = self.get_base().units.into();
|
||||
[TyF64::new(p[0], ty.clone()), TyF64::new(p[1], ty)]
|
||||
[TyF64::new(p[0], ty), TyF64::new(p[1], ty)]
|
||||
}
|
||||
|
||||
/// Where does this path segment end?
|
||||
pub fn get_to(&self) -> [TyF64; 2] {
|
||||
let p = &self.get_base().to;
|
||||
let ty: NumericType = self.get_base().units.into();
|
||||
[TyF64::new(p[0], ty.clone()), TyF64::new(p[1], ty)]
|
||||
[TyF64::new(p[0], ty), TyF64::new(p[1], ty)]
|
||||
}
|
||||
|
||||
/// The path segment start point and its type.
|
||||
|
@ -415,15 +415,41 @@ impl KclValue {
|
||||
|
||||
/// Put the point into a KCL value.
|
||||
pub fn from_point2d(p: [f64; 2], ty: NumericType, meta: Vec<Metadata>) -> Self {
|
||||
let [x, y] = p;
|
||||
Self::Tuple {
|
||||
value: vec![
|
||||
Self::Number {
|
||||
value: p[0],
|
||||
value: x,
|
||||
meta: meta.clone(),
|
||||
ty: ty.clone(),
|
||||
ty,
|
||||
},
|
||||
Self::Number {
|
||||
value: p[1],
|
||||
value: y,
|
||||
meta: meta.clone(),
|
||||
ty,
|
||||
},
|
||||
],
|
||||
meta,
|
||||
}
|
||||
}
|
||||
|
||||
/// Put the point into a KCL value.
|
||||
pub fn from_point3d(p: [f64; 3], ty: NumericType, meta: Vec<Metadata>) -> Self {
|
||||
let [x, y, z] = p;
|
||||
Self::Tuple {
|
||||
value: vec![
|
||||
Self::Number {
|
||||
value: x,
|
||||
meta: meta.clone(),
|
||||
ty,
|
||||
},
|
||||
Self::Number {
|
||||
value: y,
|
||||
meta: meta.clone(),
|
||||
ty,
|
||||
},
|
||||
Self::Number {
|
||||
value: z,
|
||||
meta: meta.clone(),
|
||||
ty,
|
||||
},
|
||||
@ -448,7 +474,7 @@ impl KclValue {
|
||||
|
||||
pub fn as_int_with_ty(&self) -> Option<(i64, NumericType)> {
|
||||
match self {
|
||||
KclValue::Number { value, ty, .. } => crate::try_f64_to_i64(*value).map(|i| (i, ty.clone())),
|
||||
KclValue::Number { value, ty, .. } => crate::try_f64_to_i64(*value).map(|i| (i, *ty)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@ -562,7 +588,7 @@ impl KclValue {
|
||||
|
||||
pub fn as_ty_f64(&self) -> Option<TyF64> {
|
||||
match self {
|
||||
KclValue::Number { value, ty, .. } => Some(TyF64::new(*value, ty.clone())),
|
||||
KclValue::Number { value, ty, .. } => Some(TyF64::new(*value, *ty)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -541,22 +541,6 @@ impl Stack {
|
||||
self.push_new_env_for_call(snapshot);
|
||||
}
|
||||
|
||||
/// Push a new stack frame on to the call stack for callees which should not read or write
|
||||
/// from memory.
|
||||
///
|
||||
/// This is suitable for calling standard library functions or other functions written in Rust
|
||||
/// which will use 'Rust memory' rather than KCL's memory and cannot reach into the wider
|
||||
/// environment.
|
||||
///
|
||||
/// Trying to read or write from this environment will panic with an index out of bounds.
|
||||
pub fn push_new_env_for_rust_call(&mut self) {
|
||||
self.call_stack.push(self.current_env);
|
||||
// Rust functions shouldn't try to set or access anything in their environment, so don't
|
||||
// waste time and space on a new env. Using usize::MAX means we'll get an overflow if we
|
||||
// try to access anything rather than a silent error.
|
||||
self.current_env = EnvironmentRef(usize::MAX, 0);
|
||||
}
|
||||
|
||||
/// Push a new stack frame on to the call stack with no connection to a parent environment.
|
||||
///
|
||||
/// Suitable for executing a separate module.
|
||||
@ -683,7 +667,7 @@ impl Stack {
|
||||
env.contains_key(var)
|
||||
}
|
||||
|
||||
/// Get a key from the first KCL (i.e., non-Rust) stack frame on the call stack.
|
||||
/// Get a key from the first stack frame on the call stack.
|
||||
pub fn get_from_call_stack(&self, key: &str, source_range: SourceRange) -> Result<(usize, &KclValue), KclError> {
|
||||
if !self.current_env.skip_env() {
|
||||
return Ok((self.current_env.1, self.get(key, source_range)?));
|
||||
@ -695,7 +679,7 @@ impl Stack {
|
||||
}
|
||||
}
|
||||
|
||||
unreachable!("It can't be Rust frames all the way down");
|
||||
unreachable!("No frames on the stack?");
|
||||
}
|
||||
|
||||
/// Iterate over all keys in the current environment which satisfy the provided predicate.
|
||||
@ -1217,24 +1201,6 @@ mod test {
|
||||
assert_get_from(mem, "c", 5, callee);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rust_env() {
|
||||
let mem = &mut Stack::new_for_tests();
|
||||
mem.add("a".to_owned(), val(1), sr()).unwrap();
|
||||
mem.add("b".to_owned(), val(3), sr()).unwrap();
|
||||
let sn = mem.snapshot();
|
||||
|
||||
mem.push_new_env_for_rust_call();
|
||||
mem.push_new_env_for_call(sn);
|
||||
assert_get(mem, "b", 3);
|
||||
mem.add("b".to_owned(), val(4), sr()).unwrap();
|
||||
assert_get(mem, "b", 4);
|
||||
|
||||
mem.pop_env();
|
||||
mem.pop_env();
|
||||
assert_get(mem, "b", 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deep_call_env() {
|
||||
let mem = &mut Stack::new_for_tests();
|
||||
|
@ -1920,6 +1920,22 @@ shape = layer() |> patternTransform(instances = 10, transform = transform)
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn pass_std_to_std() {
|
||||
let ast = r#"sketch001 = startSketchOn(XY)
|
||||
profile001 = circle(sketch001, center = [0, 0], radius = 2)
|
||||
extrude001 = extrude(profile001, length = 5)
|
||||
extrudes = patternLinear3d(
|
||||
extrude001,
|
||||
instances = 3,
|
||||
distance = 5,
|
||||
axis = [1, 1, 0],
|
||||
)
|
||||
clone001 = map(extrudes, f = clone)
|
||||
"#;
|
||||
parse_execute(ast).await.unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_zero_param_fn() {
|
||||
let ast = r#"sigmaAllow = 35000 // psi
|
||||
|
@ -460,7 +460,7 @@ impl fmt::Display for PrimitiveType {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum NumericType {
|
||||
@ -575,7 +575,7 @@ impl NumericType {
|
||||
match (&ty, &i.ty) {
|
||||
(Any, Default { .. }) if i.n == 0.0 => {}
|
||||
(Any, t) => {
|
||||
ty = t.clone();
|
||||
ty = *t;
|
||||
}
|
||||
(_, Unknown) | (Default { .. }, Default { .. }) => return (result, Unknown),
|
||||
|
||||
@ -598,7 +598,7 @@ impl NumericType {
|
||||
}
|
||||
|
||||
if ty == Any && !input.is_empty() {
|
||||
ty = input[0].ty.clone();
|
||||
ty = input[0].ty;
|
||||
}
|
||||
|
||||
(result, ty)
|
||||
@ -722,7 +722,7 @@ impl NumericType {
|
||||
if ty.subtype(self) {
|
||||
return Ok(KclValue::Number {
|
||||
value: *value,
|
||||
ty: ty.clone(),
|
||||
ty: *ty,
|
||||
meta: meta.clone(),
|
||||
});
|
||||
}
|
||||
@ -736,7 +736,7 @@ impl NumericType {
|
||||
|
||||
(Any, _) => Ok(KclValue::Number {
|
||||
value: *value,
|
||||
ty: self.clone(),
|
||||
ty: *self,
|
||||
meta: meta.clone(),
|
||||
}),
|
||||
|
||||
@ -744,7 +744,7 @@ impl NumericType {
|
||||
// means accept any number rather than force the current default.
|
||||
(_, Default { .. }) => Ok(KclValue::Number {
|
||||
value: *value,
|
||||
ty: ty.clone(),
|
||||
ty: *ty,
|
||||
meta: meta.clone(),
|
||||
}),
|
||||
|
||||
@ -840,6 +840,18 @@ pub enum UnitType {
|
||||
Angle(UnitAngle),
|
||||
}
|
||||
|
||||
impl UnitType {
|
||||
pub(crate) fn to_suffix(self) -> Option<String> {
|
||||
match self {
|
||||
UnitType::Count => Some("_".to_owned()),
|
||||
UnitType::Length(UnitLen::Unknown) => None,
|
||||
UnitType::Angle(UnitAngle::Unknown) => None,
|
||||
UnitType::Length(l) => Some(l.to_string()),
|
||||
UnitType::Angle(a) => Some(a.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for UnitType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
@ -1479,7 +1491,7 @@ impl KclValue {
|
||||
pub fn principal_type(&self) -> Option<RuntimeType> {
|
||||
match self {
|
||||
KclValue::Bool { .. } => Some(RuntimeType::Primitive(PrimitiveType::Boolean)),
|
||||
KclValue::Number { ty, .. } => Some(RuntimeType::Primitive(PrimitiveType::Number(ty.clone()))),
|
||||
KclValue::Number { ty, .. } => Some(RuntimeType::Primitive(PrimitiveType::Number(*ty))),
|
||||
KclValue::String { .. } => Some(RuntimeType::Primitive(PrimitiveType::String)),
|
||||
KclValue::Object { value, .. } => {
|
||||
let properties = value
|
||||
|
Reference in New Issue
Block a user