| @ -20,7 +20,7 @@ statement[@isGroup=Statement] { | ||||
|   ImportStatement { kw<"import"> ImportItems ImportFrom String } | | ||||
|   FunctionDeclaration { kw<"export">? kw<"fn"> VariableDefinition Equals? ParamList Arrow? Body } | | ||||
|   VariableDeclaration { kw<"export">? (kw<"var"> | kw<"let"> | kw<"const">)? VariableDefinition Equals expression } | | ||||
|   TypeDeclaration { kw<"export">? kw<"type"> identifier } | | ||||
|   TypeDeclaration { kw<"export">? kw<"type"> identifier ("=" type)? } | | ||||
|   ReturnStatement { kw<"return"> expression } | | ||||
|   ExpressionStatement { expression } | | ||||
|   Annotation { AnnotationName AnnotationList? } | ||||
| @ -79,7 +79,7 @@ type[@isGroup=Type] { | ||||
|     identifier, | ||||
|     "bool" | "number" | "string" | "tag" | "Sketch" | "SketchSurface" | "Solid" | "Plane" | ||||
|   > | | ||||
|   ArrayType { type !member "[" "]" } | | ||||
|   ArrayType { "[" type !member (";" Number "+"?)? "]" } | | ||||
|   ObjectType { "{" commaSep<ObjectProperty { PropertyName ":" type }> "}" } | ||||
| } | ||||
|  | ||||
| @ -137,7 +137,7 @@ commaSep1NoTrailingComma<term> { term ("," term)* } | ||||
|   "(" ")" | ||||
|   "{" "}" | ||||
|   "[" "]" | ||||
|   "," "?" ":" "." ".." | ||||
|   "," "?" ":" "." ".." ";" | ||||
| } | ||||
|  | ||||
| @external propSource kclHighlight from "./highlight" | ||||
|  | ||||
| @ -29,6 +29,8 @@ use crate::{ | ||||
|     CompilationError, | ||||
| }; | ||||
|  | ||||
| use super::kcl_value::TypeDef; | ||||
|  | ||||
| enum StatementKind<'a> { | ||||
|     Declaration { name: &'a str }, | ||||
|     Expression, | ||||
| @ -304,8 +306,9 @@ impl ExecutorContext { | ||||
|                                     })); | ||||
|                                 } | ||||
|                             }; | ||||
|                             let (t, props) = crate::std::std_ty(std_path, &ty.name.name); | ||||
|                             let value = KclValue::Type { | ||||
|                                 value: Some(crate::std::std_ty(std_path, &ty.name.name)), | ||||
|                                 value: TypeDef::RustRepr(t, props), | ||||
|                                 meta: vec![metadata], | ||||
|                             }; | ||||
|                             exec_state | ||||
| @ -324,12 +327,40 @@ impl ExecutorContext { | ||||
|                         } | ||||
|                         // Do nothing for primitive types, they get special treatment and their declarations are just for documentation. | ||||
|                         annotations::Impl::Primitive => {} | ||||
|                         annotations::Impl::Kcl => { | ||||
|                             return Err(KclError::Semantic(KclErrorDetails { | ||||
|                                 message: "User-defined types are not yet supported.".to_owned(), | ||||
|                                 source_ranges: vec![metadata.source_range], | ||||
|                             })); | ||||
|                         } | ||||
|                         annotations::Impl::Kcl => match &ty.alias { | ||||
|                             Some(alias) => { | ||||
|                                 let value = KclValue::Type { | ||||
|                                     value: TypeDef::Alias( | ||||
|                                         RuntimeType::from_parsed( | ||||
|                                             alias.inner.clone(), | ||||
|                                             exec_state, | ||||
|                                             metadata.source_range, | ||||
|                                         ) | ||||
|                                         .map_err(|e| KclError::Semantic(e.into()))?, | ||||
|                                     ), | ||||
|                                     meta: vec![metadata], | ||||
|                                 }; | ||||
|                                 exec_state | ||||
|                                     .mut_stack() | ||||
|                                     .add( | ||||
|                                         format!("{}{}", memory::TYPE_PREFIX, ty.name.name), | ||||
|                                         value, | ||||
|                                         metadata.source_range, | ||||
|                                     ) | ||||
|                                     .map_err(|_| { | ||||
|                                         KclError::Semantic(KclErrorDetails { | ||||
|                                             message: format!("Redefinition of type {}.", ty.name.name), | ||||
|                                             source_ranges: vec![metadata.source_range], | ||||
|                                         }) | ||||
|                                     })?; | ||||
|                             } | ||||
|                             None => { | ||||
|                                 return Err(KclError::Semantic(KclErrorDetails { | ||||
|                                     message: "User-defined types are not yet supported.".to_owned(), | ||||
|                                     source_ranges: vec![metadata.source_range], | ||||
|                                 })) | ||||
|                             } | ||||
|                         }, | ||||
|                     } | ||||
|  | ||||
|                     last_expr = None; | ||||
| @ -646,30 +677,28 @@ impl ExecutorContext { | ||||
|                 let result = self | ||||
|                     .execute_expr(&expr.expr, exec_state, metadata, &[], statement_kind) | ||||
|                     .await?; | ||||
|                 coerce(&result, &expr.ty, exec_state).ok_or_else(|| { | ||||
|                     KclError::Semantic(KclErrorDetails { | ||||
|                         message: format!( | ||||
|                             "could not coerce {} value to type {}", | ||||
|                             result.human_friendly_type(), | ||||
|                             expr.ty | ||||
|                         ), | ||||
|                         source_ranges: vec![expr.into()], | ||||
|                     }) | ||||
|                 })? | ||||
|                 coerce(&result, &expr.ty, exec_state, expr.into())? | ||||
|             } | ||||
|         }; | ||||
|         Ok(item) | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn coerce(value: &KclValue, ty: &Node<Type>, exec_state: &mut ExecState) -> Option<KclValue> { | ||||
| fn coerce( | ||||
|     value: &KclValue, | ||||
|     ty: &Node<Type>, | ||||
|     exec_state: &mut ExecState, | ||||
|     source_range: SourceRange, | ||||
| ) -> Result<KclValue, KclError> { | ||||
|     let ty = RuntimeType::from_parsed(ty.inner.clone(), exec_state, value.into()) | ||||
|         .map_err(|e| { | ||||
|             exec_state.err(e); | ||||
|         }) | ||||
|         .ok()??; | ||||
|         .map_err(|e| KclError::Semantic(e.into()))?; | ||||
|  | ||||
|     value.coerce(&ty, exec_state) | ||||
|     value.coerce(&ty, exec_state).ok_or_else(|| { | ||||
|         KclError::Semantic(KclErrorDetails { | ||||
|             message: format!("could not coerce {} value to type {}", value.human_friendly_type(), ty), | ||||
|             source_ranges: vec![source_range], | ||||
|         }) | ||||
|     }) | ||||
| } | ||||
|  | ||||
| impl BinaryPart { | ||||
|  | ||||
| @ -104,7 +104,7 @@ impl From<SolidOrSketchOrImportedGeometry> for crate::execution::KclValue { | ||||
|                             .into_iter() | ||||
|                             .map(|s| crate::execution::KclValue::Solid { value: Box::new(s) }) | ||||
|                             .collect(), | ||||
|                         ty: crate::execution::PrimitiveType::Solid, | ||||
|                         ty: crate::execution::kcl_value::RuntimeType::solid(), | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
| @ -65,7 +65,7 @@ pub enum KclValue { | ||||
|         value: Vec<KclValue>, | ||||
|         // The type of values, not the array type. | ||||
|         #[serde(skip)] | ||||
|         ty: PrimitiveType, | ||||
|         ty: RuntimeType, | ||||
|     }, | ||||
|     Object { | ||||
|         value: KclObjectFields, | ||||
| @ -105,7 +105,7 @@ pub enum KclValue { | ||||
|     #[ts(skip)] | ||||
|     Type { | ||||
|         #[serde(skip)] | ||||
|         value: Option<(PrimitiveType, StdFnProps)>, | ||||
|         value: TypeDef, | ||||
|         #[serde(skip)] | ||||
|         meta: Vec<Metadata>, | ||||
|     }, | ||||
| @ -142,6 +142,12 @@ impl JsonSchema for FunctionSource { | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Clone, PartialEq)] | ||||
| pub enum TypeDef { | ||||
|     RustRepr(PrimitiveType, StdFnProps), | ||||
|     Alias(RuntimeType), | ||||
| } | ||||
|  | ||||
| impl From<Vec<Sketch>> for KclValue { | ||||
|     fn from(mut eg: Vec<Sketch>) -> Self { | ||||
|         if eg.len() == 1 { | ||||
| @ -154,7 +160,7 @@ impl From<Vec<Sketch>> for KclValue { | ||||
|                     .into_iter() | ||||
|                     .map(|s| KclValue::Sketch { value: Box::new(s) }) | ||||
|                     .collect(), | ||||
|                 ty: crate::execution::PrimitiveType::Sketch, | ||||
|                 ty: RuntimeType::Primitive(PrimitiveType::Sketch), | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| @ -169,7 +175,7 @@ impl From<Vec<Solid>> for KclValue { | ||||
|         } else { | ||||
|             KclValue::HomArray { | ||||
|                 value: eg.into_iter().map(|s| KclValue::Solid { value: Box::new(s) }).collect(), | ||||
|                 ty: crate::execution::PrimitiveType::Solid, | ||||
|                 ty: RuntimeType::Primitive(PrimitiveType::Solid), | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| @ -635,10 +641,15 @@ impl KclValue { | ||||
|                 KclValue::ImportedGeometry { .. } => Some(value.clone()), | ||||
|                 _ => None, | ||||
|             }, | ||||
|             PrimitiveType::Tag => match value { | ||||
|                 KclValue::TagDeclarator { .. } => Some(value.clone()), | ||||
|                 KclValue::TagIdentifier { .. } => Some(value.clone()), | ||||
|                 _ => None, | ||||
|             }, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn coerce_to_array_type(&self, ty: &PrimitiveType, len: ArrayLen, exec_state: &mut ExecState) -> Option<KclValue> { | ||||
|     fn coerce_to_array_type(&self, ty: &RuntimeType, len: ArrayLen, exec_state: &mut ExecState) -> Option<KclValue> { | ||||
|         match self { | ||||
|             KclValue::HomArray { value, ty: aty } => { | ||||
|                 // TODO could check types of values individually | ||||
| @ -685,10 +696,9 @@ impl KclValue { | ||||
|                     } | ||||
|                 }; | ||||
|  | ||||
|                 let rt = RuntimeType::Primitive(ty.clone()); | ||||
|                 let value = value | ||||
|                     .iter() | ||||
|                     .map(|v| v.coerce(&rt, exec_state)) | ||||
|                     .map(|v| v.coerce(ty, exec_state)) | ||||
|                     .collect::<Option<Vec<_>>>()?; | ||||
|  | ||||
|                 Some(KclValue::HomArray { value, ty: ty.clone() }) | ||||
| @ -698,7 +708,7 @@ impl KclValue { | ||||
|                 ty: ty.clone(), | ||||
|             }), | ||||
|             value if len.satisfied(1) => { | ||||
|                 if value.has_type(&RuntimeType::Primitive(ty.clone())) { | ||||
|                 if value.has_type(ty) { | ||||
|                     Some(KclValue::HomArray { | ||||
|                         value: vec![value.clone()], | ||||
|                         ty: ty.clone(), | ||||
| @ -711,7 +721,7 @@ impl KclValue { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn coerce_to_tuple_type(&self, tys: &[PrimitiveType], exec_state: &mut ExecState) -> Option<KclValue> { | ||||
|     fn coerce_to_tuple_type(&self, tys: &[RuntimeType], exec_state: &mut ExecState) -> Option<KclValue> { | ||||
|         match self { | ||||
|             KclValue::MixedArray { value, .. } | KclValue::HomArray { value, .. } => { | ||||
|                 if value.len() < tys.len() { | ||||
| @ -719,7 +729,7 @@ impl KclValue { | ||||
|                 } | ||||
|                 let mut result = Vec::new(); | ||||
|                 for (i, t) in tys.iter().enumerate() { | ||||
|                     result.push(value[i].coerce_to_primitive_type(t, exec_state)?); | ||||
|                     result.push(value[i].coerce(t, exec_state)?); | ||||
|                 } | ||||
|  | ||||
|                 Some(KclValue::MixedArray { | ||||
| @ -732,7 +742,7 @@ impl KclValue { | ||||
|                 meta: meta.clone(), | ||||
|             }), | ||||
|             value if tys.len() == 1 => { | ||||
|                 if value.has_type(&RuntimeType::Primitive(tys[0].clone())) { | ||||
|                 if value.has_type(&tys[0]) { | ||||
|                     Some(KclValue::MixedArray { | ||||
|                         value: vec![value.clone()], | ||||
|                         meta: Vec::new(), | ||||
| @ -788,18 +798,15 @@ impl KclValue { | ||||
|             KclValue::Solid { .. } => Some(RuntimeType::Primitive(PrimitiveType::Solid)), | ||||
|             KclValue::ImportedGeometry(..) => Some(RuntimeType::Primitive(PrimitiveType::ImportedGeometry)), | ||||
|             KclValue::MixedArray { value, .. } => Some(RuntimeType::Tuple( | ||||
|                 value | ||||
|                     .iter() | ||||
|                     .map(|v| v.principal_type().and_then(RuntimeType::primitive)) | ||||
|                     .collect::<Option<Vec<_>>>()?, | ||||
|                 value.iter().map(|v| v.principal_type()).collect::<Option<Vec<_>>>()?, | ||||
|             )), | ||||
|             KclValue::HomArray { ty, value, .. } => Some(RuntimeType::Array(ty.clone(), ArrayLen::Known(value.len()))), | ||||
|             KclValue::Face { .. } => None, | ||||
|             KclValue::Helix { .. } | ||||
|             | KclValue::Function { .. } | ||||
|             KclValue::HomArray { ty, value, .. } => { | ||||
|                 Some(RuntimeType::Array(Box::new(ty.clone()), ArrayLen::Known(value.len()))) | ||||
|             } | ||||
|             KclValue::TagIdentifier(_) | KclValue::TagDeclarator(_) => Some(RuntimeType::Primitive(PrimitiveType::Tag)), | ||||
|             KclValue::Face { .. } | KclValue::Helix { .. } => None, | ||||
|             KclValue::Function { .. } | ||||
|             | KclValue::Module { .. } | ||||
|             | KclValue::TagIdentifier(_) | ||||
|             | KclValue::TagDeclarator(_) | ||||
|             | KclValue::KclNone { .. } | ||||
|             | KclValue::Type { .. } | ||||
|             | KclValue::Uuid { .. } => None, | ||||
| @ -923,42 +930,89 @@ impl KclValue { | ||||
| #[derive(Debug, Clone, PartialEq)] | ||||
| pub enum RuntimeType { | ||||
|     Primitive(PrimitiveType), | ||||
|     Array(PrimitiveType, ArrayLen), | ||||
|     Array(Box<RuntimeType>, ArrayLen), | ||||
|     Union(Vec<RuntimeType>), | ||||
|     Tuple(Vec<PrimitiveType>), | ||||
|     Tuple(Vec<RuntimeType>), | ||||
|     Object(Vec<(String, RuntimeType)>), | ||||
| } | ||||
|  | ||||
| impl RuntimeType { | ||||
|     /// `[Sketch; 1+]` | ||||
|     pub fn sketches() -> Self { | ||||
|         RuntimeType::Array( | ||||
|             Box::new(RuntimeType::Primitive(PrimitiveType::Sketch)), | ||||
|             ArrayLen::NonEmpty, | ||||
|         ) | ||||
|     } | ||||
|  | ||||
|     /// `[Solid; 1+]` | ||||
|     pub fn solids() -> Self { | ||||
|         RuntimeType::Array( | ||||
|             Box::new(RuntimeType::Primitive(PrimitiveType::Solid)), | ||||
|             ArrayLen::NonEmpty, | ||||
|         ) | ||||
|     } | ||||
|  | ||||
|     pub fn solid() -> Self { | ||||
|         RuntimeType::Primitive(PrimitiveType::Solid) | ||||
|     } | ||||
|  | ||||
|     pub fn imported() -> Self { | ||||
|         RuntimeType::Primitive(PrimitiveType::ImportedGeometry) | ||||
|     } | ||||
|  | ||||
|     pub fn from_parsed( | ||||
|         value: Type, | ||||
|         exec_state: &mut ExecState, | ||||
|         source_range: SourceRange, | ||||
|     ) -> Result<Option<Self>, CompilationError> { | ||||
|         Ok(match value { | ||||
|             Type::Primitive(pt) => { | ||||
|                 PrimitiveType::from_parsed(pt, exec_state, source_range)?.map(RuntimeType::Primitive) | ||||
|             } | ||||
|     ) -> Result<Self, CompilationError> { | ||||
|         match value { | ||||
|             Type::Primitive(pt) => Self::from_parsed_primitive(pt, exec_state, source_range), | ||||
|             Type::Array { ty, len } => { | ||||
|                 PrimitiveType::from_parsed(ty, exec_state, source_range)?.map(|t| RuntimeType::Array(t, len)) | ||||
|                 Self::from_parsed_primitive(ty, exec_state, source_range).map(|t| RuntimeType::Array(Box::new(t), len)) | ||||
|             } | ||||
|             Type::Union { tys } => tys | ||||
|                 .into_iter() | ||||
|                 .map(|t| PrimitiveType::from_parsed(t.inner, exec_state, source_range)) | ||||
|                 .collect::<Result<Option<Vec<_>>, CompilationError>>()? | ||||
|                 .map(|t| Self::from_parsed_primitive(t.inner, exec_state, source_range)) | ||||
|                 .collect::<Result<Vec<_>, CompilationError>>() | ||||
|                 .map(RuntimeType::Union), | ||||
|             Type::Object { properties } => properties | ||||
|                 .into_iter() | ||||
|                 .map(|p| { | ||||
|                     let pt = match p.type_ { | ||||
|                         Some(t) => t, | ||||
|                         None => return Ok(None), | ||||
|                     }; | ||||
|                     Ok(RuntimeType::from_parsed(pt.inner, exec_state, source_range)? | ||||
|                         .map(|ty| (p.identifier.inner.name, ty))) | ||||
|                     RuntimeType::from_parsed(p.type_.unwrap().inner, exec_state, source_range) | ||||
|                         .map(|ty| (p.identifier.inner.name, ty)) | ||||
|                 }) | ||||
|                 .collect::<Result<Option<Vec<_>>, CompilationError>>()? | ||||
|                 .collect::<Result<Vec<_>, CompilationError>>() | ||||
|                 .map(RuntimeType::Object), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn from_parsed_primitive( | ||||
|         value: AstPrimitiveType, | ||||
|         exec_state: &mut ExecState, | ||||
|         source_range: SourceRange, | ||||
|     ) -> Result<Self, CompilationError> { | ||||
|         Ok(match value { | ||||
|             AstPrimitiveType::String => RuntimeType::Primitive(PrimitiveType::String), | ||||
|             AstPrimitiveType::Boolean => RuntimeType::Primitive(PrimitiveType::Boolean), | ||||
|             AstPrimitiveType::Number(suffix) => RuntimeType::Primitive(PrimitiveType::Number( | ||||
|                 NumericType::from_parsed(suffix, &exec_state.mod_local.settings), | ||||
|             )), | ||||
|             AstPrimitiveType::Named(name) => { | ||||
|                 let ty_val = exec_state | ||||
|                     .stack() | ||||
|                     .get(&format!("{}{}", memory::TYPE_PREFIX, name.name), source_range) | ||||
|                     .map_err(|_| CompilationError::err(source_range, format!("Unknown type: {}", name.name)))?; | ||||
|  | ||||
|                 match ty_val { | ||||
|                     KclValue::Type { value, .. } => match value { | ||||
|                         TypeDef::RustRepr(ty, _) => RuntimeType::Primitive(ty.clone()), | ||||
|                         TypeDef::Alias(ty) => ty.clone(), | ||||
|                     }, | ||||
|                     _ => unreachable!(), | ||||
|                 } | ||||
|             } | ||||
|             AstPrimitiveType::Tag => RuntimeType::Primitive(PrimitiveType::Tag), | ||||
|         }) | ||||
|     } | ||||
|  | ||||
| @ -975,7 +1029,7 @@ impl RuntimeType { | ||||
|                 .join(" or "), | ||||
|             RuntimeType::Tuple(tys) => format!( | ||||
|                 "an array with values of types ({})", | ||||
|                 tys.iter().map(PrimitiveType::to_string).collect::<Vec<_>>().join(", ") | ||||
|                 tys.iter().map(Self::human_friendly_type).collect::<Vec<_>>().join(", ") | ||||
|             ), | ||||
|             RuntimeType::Object(_) => format!("an object with fields {}", self), | ||||
|         } | ||||
| @ -990,7 +1044,7 @@ impl RuntimeType { | ||||
|             // TODO arrays could be covariant | ||||
|             (Array(t1, l1), Array(t2, l2)) => t1 == t2 && l1.subtype(*l2), | ||||
|             (Tuple(t1), Tuple(t2)) => t1 == t2, | ||||
|             (Tuple(t1), Array(t2, l2)) => (l2.satisfied(t1.len())) && t1.iter().all(|t| t == t2), | ||||
|             (Tuple(t1), Array(t2, l2)) => (l2.satisfied(t1.len())) && t1.iter().all(|t| t == &**t2), | ||||
|             (Union(ts1), Union(ts2)) => ts1.iter().all(|t| ts2.contains(t)), | ||||
|             (t1, Union(ts2)) => ts2.contains(t1), | ||||
|             // TODO record subtyping - subtype can be larger, fields can be covariant. | ||||
| @ -999,10 +1053,17 @@ impl RuntimeType { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn primitive(self) -> Option<PrimitiveType> { | ||||
|     fn display_multiple(&self) -> String { | ||||
|         match self { | ||||
|             RuntimeType::Primitive(t) => Some(t), | ||||
|             _ => None, | ||||
|             RuntimeType::Primitive(ty) => ty.display_multiple(), | ||||
|             RuntimeType::Array(..) => "arrays".to_owned(), | ||||
|             RuntimeType::Union(tys) => tys | ||||
|                 .iter() | ||||
|                 .map(|t| t.display_multiple()) | ||||
|                 .collect::<Vec<_>>() | ||||
|                 .join(" or "), | ||||
|             RuntimeType::Tuple(_) => "arrays".to_owned(), | ||||
|             RuntimeType::Object(_) => format!("objects with fields {self}"), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1072,6 +1133,7 @@ pub enum PrimitiveType { | ||||
|     Number(NumericType), | ||||
|     String, | ||||
|     Boolean, | ||||
|     Tag, | ||||
|     Sketch, | ||||
|     Solid, | ||||
|     Plane, | ||||
| @ -1079,35 +1141,6 @@ pub enum PrimitiveType { | ||||
| } | ||||
|  | ||||
| impl PrimitiveType { | ||||
|     fn from_parsed( | ||||
|         value: AstPrimitiveType, | ||||
|         exec_state: &mut ExecState, | ||||
|         source_range: SourceRange, | ||||
|     ) -> Result<Option<Self>, CompilationError> { | ||||
|         Ok(match value { | ||||
|             AstPrimitiveType::String => Some(PrimitiveType::String), | ||||
|             AstPrimitiveType::Boolean => Some(PrimitiveType::Boolean), | ||||
|             AstPrimitiveType::Number(suffix) => Some(PrimitiveType::Number(NumericType::from_parsed( | ||||
|                 suffix, | ||||
|                 &exec_state.mod_local.settings, | ||||
|             ))), | ||||
|             AstPrimitiveType::Named(name) => { | ||||
|                 let ty_val = exec_state | ||||
|                     .stack() | ||||
|                     .get(&format!("{}{}", memory::TYPE_PREFIX, name.name), source_range) | ||||
|                     .map_err(|_| CompilationError::err(source_range, format!("Unknown type: {}", name.name)))?; | ||||
|  | ||||
|                 let (ty, _) = match ty_val { | ||||
|                     KclValue::Type { value: Some(ty), .. } => ty, | ||||
|                     _ => unreachable!(), | ||||
|                 }; | ||||
|  | ||||
|                 Some(ty.clone()) | ||||
|             } | ||||
|             _ => None, | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     fn display_multiple(&self) -> String { | ||||
|         match self { | ||||
|             PrimitiveType::Number(NumericType::Known(unit)) => format!("numbers({unit})"), | ||||
| @ -1118,6 +1151,7 @@ impl PrimitiveType { | ||||
|             PrimitiveType::Solid => "Solids".to_owned(), | ||||
|             PrimitiveType::Plane => "Planes".to_owned(), | ||||
|             PrimitiveType::ImportedGeometry => "imported geometries".to_owned(), | ||||
|             PrimitiveType::Tag => "tags".to_owned(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1129,6 +1163,7 @@ impl fmt::Display for PrimitiveType { | ||||
|             PrimitiveType::Number(_) => write!(f, "number"), | ||||
|             PrimitiveType::String => write!(f, "string"), | ||||
|             PrimitiveType::Boolean => write!(f, "bool"), | ||||
|             PrimitiveType::Tag => write!(f, "tag"), | ||||
|             PrimitiveType::Sketch => write!(f, "Sketch"), | ||||
|             PrimitiveType::Solid => write!(f, "Solid"), | ||||
|             PrimitiveType::Plane => write!(f, "Plane"), | ||||
|  | ||||
| @ -1397,6 +1397,22 @@ const answer = returnX()"#; | ||||
|         assert!(errs.is_empty()); | ||||
|     } | ||||
|  | ||||
|     #[tokio::test(flavor = "multi_thread")] | ||||
|     async fn type_aliases() { | ||||
|         let text = r#"type MyTy = [number; 2] | ||||
| fn foo(x: MyTy) { | ||||
|     return x[0] | ||||
| } | ||||
|  | ||||
| foo([0, 1]) | ||||
|  | ||||
| type Other = MyTy | string | ||||
| "#; | ||||
|         let result = parse_execute(text).await.unwrap(); | ||||
|         let errs = result.exec_state.errors(); | ||||
|         assert!(errs.is_empty()); | ||||
|     } | ||||
|  | ||||
|     #[tokio::test(flavor = "multi_thread")] | ||||
|     async fn test_cannot_shebang_in_fn() { | ||||
|         let ast = r#" | ||||
|  | ||||
| @ -312,6 +312,9 @@ impl TypeDeclaration { | ||||
|                 hasher.update(a.compute_digest()); | ||||
|             } | ||||
|         } | ||||
|         if let Some(alias) = &mut slf.alias { | ||||
|             hasher.update(alias.compute_digest()); | ||||
|         } | ||||
|     }); | ||||
| } | ||||
|  | ||||
|  | ||||
| @ -1895,6 +1895,7 @@ pub struct TypeDeclaration { | ||||
|     pub args: Option<NodeList<Identifier>>, | ||||
|     #[serde(default, skip_serializing_if = "ItemVisibility::is_default")] | ||||
|     pub visibility: ItemVisibility, | ||||
|     pub alias: Option<Node<Type>>, | ||||
|  | ||||
|     #[serde(default, skip_serializing_if = "Option::is_none")] | ||||
|     #[ts(optional)] | ||||
|  | ||||
| @ -2216,7 +2216,7 @@ fn ty_decl(i: &mut TokenSlice) -> PResult<BoxNode<TypeDeclaration>> { | ||||
|     let name = identifier(i)?; | ||||
|     let mut end = name.end; | ||||
|  | ||||
|     let args = if peek(open_paren).parse_next(i).is_ok() { | ||||
|     let args = if peek((opt(whitespace), open_paren)).parse_next(i).is_ok() { | ||||
|         ignore_whitespace(i); | ||||
|         open_paren(i)?; | ||||
|         ignore_whitespace(i); | ||||
| @ -2229,11 +2229,28 @@ fn ty_decl(i: &mut TokenSlice) -> PResult<BoxNode<TypeDeclaration>> { | ||||
|         None | ||||
|     }; | ||||
|  | ||||
|     let alias = if peek((opt(whitespace), equals)).parse_next(i).is_ok() { | ||||
|         ignore_whitespace(i); | ||||
|         equals(i)?; | ||||
|         ignore_whitespace(i); | ||||
|         let ty = argument_type(i)?; | ||||
|  | ||||
|         ParseContext::warn(CompilationError::err( | ||||
|             ty.as_source_range(), | ||||
|             "Type aliases are experimental, likely to change in the future, and likely to not work properly.", | ||||
|         )); | ||||
|  | ||||
|         Some(ty) | ||||
|     } else { | ||||
|         None | ||||
|     }; | ||||
|  | ||||
|     let module_id = name.module_id; | ||||
|     let result = Node::boxed( | ||||
|         TypeDeclaration { | ||||
|             name, | ||||
|             args, | ||||
|             alias, | ||||
|             visibility, | ||||
|             digest: None, | ||||
|         }, | ||||
| @ -2686,13 +2703,21 @@ fn argument_type(i: &mut TokenSlice) -> PResult<Node<Type>> { | ||||
|     let type_ = alt(( | ||||
|         // Object types | ||||
|         // TODO it is buggy to treat object fields like parameters since the parameters parser assumes a terminating `)`. | ||||
|         (open_brace, parameters, close_brace).map(|(open, params, close)| { | ||||
|             Node::new( | ||||
|         (open_brace, parameters, close_brace).try_map(|(open, params, close)| { | ||||
|             for p in ¶ms { | ||||
|                 if p.type_.is_none() { | ||||
|                     return Err(CompilationError::fatal( | ||||
|                         p.identifier.as_source_range(), | ||||
|                         "Missing type for field in record type", | ||||
|                     )); | ||||
|                 } | ||||
|             } | ||||
|             Ok(Node::new( | ||||
|                 Type::Object { properties: params }, | ||||
|                 open.start, | ||||
|                 close.end, | ||||
|                 open.module_id, | ||||
|             ) | ||||
|             )) | ||||
|         }), | ||||
|         // Array types | ||||
|         array_type, | ||||
|  | ||||
| @ -12,10 +12,7 @@ use validator::Validate; | ||||
|  | ||||
| use crate::{ | ||||
|     errors::{KclError, KclErrorDetails}, | ||||
|     execution::{ | ||||
|         kcl_value::{ArrayLen, RuntimeType}, | ||||
|         ExecState, KclValue, PrimitiveType, Solid, | ||||
|     }, | ||||
|     execution::{kcl_value::RuntimeType, ExecState, KclValue, Solid}, | ||||
|     std::Args, | ||||
| }; | ||||
|  | ||||
| @ -42,11 +39,7 @@ struct AppearanceData { | ||||
|  | ||||
| /// Set the appearance of a solid. This only works on solids, not sketches or individual paths. | ||||
| pub async fn appearance(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { | ||||
|     let solids = args.get_unlabeled_kw_arg_typed( | ||||
|         "solids", | ||||
|         &RuntimeType::Array(PrimitiveType::Solid, ArrayLen::NonEmpty), | ||||
|         exec_state, | ||||
|     )?; | ||||
|     let solids = args.get_unlabeled_kw_arg_typed("solids", &RuntimeType::solids(), exec_state)?; | ||||
|  | ||||
|     let color: String = args.get_kw_arg("color")?; | ||||
|     let metalness: Option<f64> = args.get_kw_arg_opt("metalness")?; | ||||
|  | ||||
| @ -12,7 +12,7 @@ use serde::{Deserialize, Serialize}; | ||||
| use crate::{ | ||||
|     errors::{KclError, KclErrorDetails}, | ||||
|     execution::{ | ||||
|         kcl_value::{ArrayLen, FunctionSource, NumericType, RuntimeType}, | ||||
|         kcl_value::{FunctionSource, NumericType, RuntimeType}, | ||||
|         ExecState, ExecutorContext, ExtrudeSurface, Helix, KclObjectFields, KclValue, Metadata, PrimitiveType, Sketch, | ||||
|         SketchSurface, Solid, TagIdentifier, | ||||
|     }, | ||||
| @ -309,8 +309,10 @@ impl Args { | ||||
|                 ty.human_friendly_type(), | ||||
|             ); | ||||
|             let suggestion = match (ty, actual_type_name) { | ||||
|                 (RuntimeType::Primitive(PrimitiveType::Solid), "Sketch") | ||||
|                 | (RuntimeType::Array(PrimitiveType::Solid, _), "Sketch") => Some( | ||||
|                 (RuntimeType::Primitive(PrimitiveType::Solid), "Sketch") => Some( | ||||
|                     "You can convert a sketch (2D) into a Solid (3D) by calling a function like `extrude` or `revolve`", | ||||
|                 ), | ||||
|                 (RuntimeType::Array(ty, _), "Sketch") if **ty == RuntimeType::Primitive(PrimitiveType::Solid) => Some( | ||||
|                     "You can convert a sketch (2D) into a Solid (3D) by calling a function like `extrude` or `revolve`", | ||||
|                 ), | ||||
|                 _ => None, | ||||
| @ -597,7 +599,7 @@ impl Args { | ||||
|         }; | ||||
|         let sarg = arg0 | ||||
|             .value | ||||
|             .coerce(&RuntimeType::Array(PrimitiveType::Sketch, ArrayLen::None), exec_state) | ||||
|             .coerce(&RuntimeType::sketches(), exec_state) | ||||
|             .ok_or(KclError::Type(KclErrorDetails { | ||||
|                 message: format!( | ||||
|                     "Expected an array of sketches, found {}", | ||||
| @ -685,7 +687,7 @@ impl Args { | ||||
|         }; | ||||
|         let sarg = arg1 | ||||
|             .value | ||||
|             .coerce(&RuntimeType::Array(PrimitiveType::Sketch, ArrayLen::None), exec_state) | ||||
|             .coerce(&RuntimeType::sketches(), exec_state) | ||||
|             .ok_or(KclError::Type(KclErrorDetails { | ||||
|                 message: format!( | ||||
|                     "Expected one or more sketches for second argument, found {}", | ||||
|  | ||||
| @ -19,8 +19,8 @@ use uuid::Uuid; | ||||
| use crate::{ | ||||
|     errors::{KclError, KclErrorDetails}, | ||||
|     execution::{ | ||||
|         kcl_value::{ArrayLen, RuntimeType}, | ||||
|         ArtifactId, ExecState, ExtrudeSurface, GeoMeta, KclValue, Path, PrimitiveType, Sketch, SketchSurface, Solid, | ||||
|         kcl_value::RuntimeType, ArtifactId, ExecState, ExtrudeSurface, GeoMeta, KclValue, Path, Sketch, SketchSurface, | ||||
|         Solid, | ||||
|     }, | ||||
|     parsing::ast::types::TagNode, | ||||
|     std::Args, | ||||
| @ -28,11 +28,7 @@ use crate::{ | ||||
|  | ||||
| /// Extrudes by a given amount. | ||||
| pub async fn extrude(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { | ||||
|     let sketches = args.get_unlabeled_kw_arg_typed( | ||||
|         "sketches", | ||||
|         &RuntimeType::Array(PrimitiveType::Sketch, ArrayLen::NonEmpty), | ||||
|         exec_state, | ||||
|     )?; | ||||
|     let sketches = args.get_unlabeled_kw_arg_typed("sketches", &RuntimeType::sketches(), exec_state)?; | ||||
|     let length = args.get_kw_arg("length")?; | ||||
|     let tag_start = args.get_kw_arg_opt("tagStart")?; | ||||
|     let tag_end = args.get_kw_arg_opt("tagEnd")?; | ||||
|  | ||||
| @ -9,11 +9,7 @@ use kittycad_modeling_cmds as kcmc; | ||||
|  | ||||
| use crate::{ | ||||
|     errors::{KclError, KclErrorDetails}, | ||||
|     execution::{ | ||||
|         kcl_value::{ArrayLen, RuntimeType}, | ||||
|         ExecState, KclValue, PrimitiveType, Sketch, Solid, | ||||
|     }, | ||||
|     parsing::ast::types::TagNode, | ||||
|     execution::{kcl_value::RuntimeType, ExecState, KclValue, Sketch, Solid}, | ||||
|     std::{extrude::do_post_extrude, fillet::default_tolerance, Args}, | ||||
| }; | ||||
|  | ||||
| @ -21,11 +17,7 @@ const DEFAULT_V_DEGREE: u32 = 2; | ||||
|  | ||||
| /// Create a 3D surface or solid by interpolating between two or more sketches. | ||||
| pub async fn loft(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { | ||||
|     let sketches = args.get_unlabeled_kw_arg_typed( | ||||
|         "sketches", | ||||
|         &RuntimeType::Array(PrimitiveType::Sketch, ArrayLen::NonEmpty), | ||||
|         exec_state, | ||||
|     )?; | ||||
|     let sketches = args.get_unlabeled_kw_arg_typed("sketches", &RuntimeType::sketches(), exec_state)?; | ||||
|     let v_degree: NonZeroU32 = args | ||||
|         .get_kw_arg_opt("vDegree")? | ||||
|         .unwrap_or(NonZeroU32::new(DEFAULT_V_DEGREE).unwrap()); | ||||
|  | ||||
| @ -20,8 +20,8 @@ use super::args::Arg; | ||||
| use crate::{ | ||||
|     errors::{KclError, KclErrorDetails}, | ||||
|     execution::{ | ||||
|         kcl_value::{ArrayLen, FunctionSource, NumericType, RuntimeType}, | ||||
|         ExecState, Geometries, Geometry, KclObjectFields, KclValue, Point2d, Point3d, PrimitiveType, Sketch, Solid, | ||||
|         kcl_value::{FunctionSource, NumericType, RuntimeType}, | ||||
|         ExecState, Geometries, Geometry, KclObjectFields, KclValue, Point2d, Point3d, Sketch, Solid, | ||||
|     }, | ||||
|     std::Args, | ||||
|     ExecutorContext, SourceRange, | ||||
| @ -47,11 +47,7 @@ pub struct LinearPattern3dData { | ||||
|  | ||||
| /// Repeat some 3D solid, changing each repetition slightly. | ||||
| pub async fn pattern_transform(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { | ||||
|     let solids = args.get_unlabeled_kw_arg_typed( | ||||
|         "solids", | ||||
|         &RuntimeType::Array(PrimitiveType::Solid, ArrayLen::NonEmpty), | ||||
|         exec_state, | ||||
|     )?; | ||||
|     let solids = args.get_unlabeled_kw_arg_typed("solids", &RuntimeType::solids(), exec_state)?; | ||||
|     let instances: u32 = args.get_kw_arg("instances")?; | ||||
|     let transform: &FunctionSource = args.get_kw_arg("transform")?; | ||||
|     let use_original: Option<bool> = args.get_kw_arg_opt("useOriginal")?; | ||||
| @ -62,11 +58,7 @@ pub async fn pattern_transform(exec_state: &mut ExecState, args: Args) -> Result | ||||
|  | ||||
| /// Repeat some 2D sketch, changing each repetition slightly. | ||||
| pub async fn pattern_transform_2d(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { | ||||
|     let sketches = args.get_unlabeled_kw_arg_typed( | ||||
|         "sketches", | ||||
|         &RuntimeType::Array(PrimitiveType::Sketch, ArrayLen::NonEmpty), | ||||
|         exec_state, | ||||
|     )?; | ||||
|     let sketches = args.get_unlabeled_kw_arg_typed("sketches", &RuntimeType::sketches(), exec_state)?; | ||||
|     let instances: u32 = args.get_kw_arg("instances")?; | ||||
|     let transform: &FunctionSource = args.get_kw_arg("transform")?; | ||||
|     let use_original: Option<bool> = args.get_kw_arg_opt("useOriginal")?; | ||||
| @ -696,11 +688,7 @@ mod tests { | ||||
|  | ||||
| /// A linear pattern on a 2D sketch. | ||||
| pub async fn pattern_linear_2d(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { | ||||
|     let sketches = args.get_unlabeled_kw_arg_typed( | ||||
|         "sketches", | ||||
|         &RuntimeType::Array(PrimitiveType::Sketch, ArrayLen::NonEmpty), | ||||
|         exec_state, | ||||
|     )?; | ||||
|     let sketches = args.get_unlabeled_kw_arg_typed("sketches", &RuntimeType::sketches(), exec_state)?; | ||||
|     let instances: u32 = args.get_kw_arg("instances")?; | ||||
|     let distance: f64 = args.get_kw_arg("distance")?; | ||||
|     let axis: [f64; 2] = args.get_kw_arg("axis")?; | ||||
| @ -779,11 +767,7 @@ async fn inner_pattern_linear_2d( | ||||
|  | ||||
| /// A linear pattern on a 3D model. | ||||
| pub async fn pattern_linear_3d(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { | ||||
|     let solids = args.get_unlabeled_kw_arg_typed( | ||||
|         "solids", | ||||
|         &RuntimeType::Array(PrimitiveType::Solid, ArrayLen::NonEmpty), | ||||
|         exec_state, | ||||
|     )?; | ||||
|     let solids = args.get_unlabeled_kw_arg_typed("solids", &RuntimeType::solids(), exec_state)?; | ||||
|     let instances: u32 = args.get_kw_arg("instances")?; | ||||
|     let distance: f64 = args.get_kw_arg("distance")?; | ||||
|     let axis: [f64; 3] = args.get_kw_arg("axis")?; | ||||
| @ -1028,11 +1012,7 @@ impl CircularPattern { | ||||
|  | ||||
| /// A circular pattern on a 2D sketch. | ||||
| pub async fn pattern_circular_2d(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { | ||||
|     let sketches = args.get_unlabeled_kw_arg_typed( | ||||
|         "sketches", | ||||
|         &RuntimeType::Array(PrimitiveType::Sketch, ArrayLen::NonEmpty), | ||||
|         exec_state, | ||||
|     )?; | ||||
|     let sketches = args.get_unlabeled_kw_arg_typed("sketches", &RuntimeType::sketches(), exec_state)?; | ||||
|     let instances: u32 = args.get_kw_arg("instances")?; | ||||
|     let center: [f64; 2] = args.get_kw_arg("center")?; | ||||
|     let arc_degrees: f64 = args.get_kw_arg("arcDegrees")?; | ||||
| @ -1136,11 +1116,7 @@ async fn inner_pattern_circular_2d( | ||||
|  | ||||
| /// A circular pattern on a 3D model. | ||||
| pub async fn pattern_circular_3d(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { | ||||
|     let solids = args.get_unlabeled_kw_arg_typed( | ||||
|         "solids", | ||||
|         &RuntimeType::Array(PrimitiveType::Solid, ArrayLen::NonEmpty), | ||||
|         exec_state, | ||||
|     )?; | ||||
|     let solids = args.get_unlabeled_kw_arg_typed("solids", &RuntimeType::solids(), exec_state)?; | ||||
|     // The number of total instances. Must be greater than or equal to 1. | ||||
|     // This includes the original entity. For example, if instances is 2, | ||||
|     // there will be two copies -- the original, and one new copy. | ||||
|  | ||||
| @ -7,20 +7,13 @@ use kittycad_modeling_cmds as kcmc; | ||||
|  | ||||
| use crate::{ | ||||
|     errors::{KclError, KclErrorDetails}, | ||||
|     execution::{ | ||||
|         kcl_value::{ArrayLen, RuntimeType}, | ||||
|         ExecState, KclValue, PrimitiveType, Solid, | ||||
|     }, | ||||
|     execution::{kcl_value::RuntimeType, ExecState, KclValue, Solid}, | ||||
|     std::{sketch::FaceTag, Args}, | ||||
| }; | ||||
|  | ||||
| /// Create a shell. | ||||
| pub async fn shell(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { | ||||
|     let solids = args.get_unlabeled_kw_arg_typed( | ||||
|         "solids", | ||||
|         &RuntimeType::Array(PrimitiveType::Solid, ArrayLen::NonEmpty), | ||||
|         exec_state, | ||||
|     )?; | ||||
|     let solids = args.get_unlabeled_kw_arg_typed("solids", &RuntimeType::solids(), exec_state)?; | ||||
|     let thickness = args.get_kw_arg("thickness")?; | ||||
|     let faces = args.get_kw_arg("faces")?; | ||||
|  | ||||
|  | ||||
| @ -9,10 +9,7 @@ use serde::{Deserialize, Serialize}; | ||||
|  | ||||
| use crate::{ | ||||
|     errors::KclError, | ||||
|     execution::{ | ||||
|         kcl_value::{ArrayLen, RuntimeType}, | ||||
|         ExecState, Helix, KclValue, PrimitiveType, Sketch, Solid, | ||||
|     }, | ||||
|     execution::{kcl_value::RuntimeType, ExecState, Helix, KclValue, Sketch, Solid}, | ||||
|     parsing::ast::types::TagNode, | ||||
|     std::{extrude::do_post_extrude, fillet::default_tolerance, Args}, | ||||
| }; | ||||
| @ -28,11 +25,7 @@ pub enum SweepPath { | ||||
|  | ||||
| /// Extrude a sketch along a path. | ||||
| pub async fn sweep(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { | ||||
|     let sketches = args.get_unlabeled_kw_arg_typed( | ||||
|         "sketches", | ||||
|         &RuntimeType::Array(PrimitiveType::Sketch, ArrayLen::NonEmpty), | ||||
|         exec_state, | ||||
|     )?; | ||||
|     let sketches = args.get_unlabeled_kw_arg_typed("sketches", &RuntimeType::sketches(), exec_state)?; | ||||
|     let path: SweepPath = args.get_kw_arg("path")?; | ||||
|     let sectional = args.get_kw_arg_opt("sectional")?; | ||||
|     let tolerance = args.get_kw_arg_opt("tolerance")?; | ||||
|  | ||||
| @ -13,10 +13,7 @@ use kittycad_modeling_cmds as kcmc; | ||||
|  | ||||
| use crate::{ | ||||
|     errors::{KclError, KclErrorDetails}, | ||||
|     execution::{ | ||||
|         kcl_value::{ArrayLen, RuntimeType}, | ||||
|         ExecState, KclValue, PrimitiveType, SolidOrSketchOrImportedGeometry, | ||||
|     }, | ||||
|     execution::{kcl_value::RuntimeType, ExecState, KclValue, SolidOrSketchOrImportedGeometry}, | ||||
|     std::Args, | ||||
| }; | ||||
|  | ||||
| @ -25,9 +22,9 @@ pub async fn scale(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K | ||||
|     let objects = args.get_unlabeled_kw_arg_typed( | ||||
|         "objects", | ||||
|         &RuntimeType::Union(vec![ | ||||
|             RuntimeType::Array(PrimitiveType::Sketch, ArrayLen::NonEmpty), | ||||
|             RuntimeType::Array(PrimitiveType::Solid, ArrayLen::NonEmpty), | ||||
|             RuntimeType::Primitive(PrimitiveType::ImportedGeometry), | ||||
|             RuntimeType::sketches(), | ||||
|             RuntimeType::solids(), | ||||
|             RuntimeType::imported(), | ||||
|         ]), | ||||
|         exec_state, | ||||
|     )?; | ||||
| @ -187,9 +184,9 @@ pub async fn translate(exec_state: &mut ExecState, args: Args) -> Result<KclValu | ||||
|     let objects = args.get_unlabeled_kw_arg_typed( | ||||
|         "objects", | ||||
|         &RuntimeType::Union(vec![ | ||||
|             RuntimeType::Array(PrimitiveType::Sketch, ArrayLen::NonEmpty), | ||||
|             RuntimeType::Array(PrimitiveType::Solid, ArrayLen::NonEmpty), | ||||
|             RuntimeType::Primitive(PrimitiveType::ImportedGeometry), | ||||
|             RuntimeType::sketches(), | ||||
|             RuntimeType::solids(), | ||||
|             RuntimeType::imported(), | ||||
|         ]), | ||||
|         exec_state, | ||||
|     )?; | ||||
| @ -390,9 +387,9 @@ pub async fn rotate(exec_state: &mut ExecState, args: Args) -> Result<KclValue, | ||||
|     let objects = args.get_unlabeled_kw_arg_typed( | ||||
|         "objects", | ||||
|         &RuntimeType::Union(vec![ | ||||
|             RuntimeType::Array(PrimitiveType::Sketch, ArrayLen::NonEmpty), | ||||
|             RuntimeType::Array(PrimitiveType::Solid, ArrayLen::NonEmpty), | ||||
|             RuntimeType::Primitive(PrimitiveType::ImportedGeometry), | ||||
|             RuntimeType::sketches(), | ||||
|             RuntimeType::solids(), | ||||
|             RuntimeType::imported(), | ||||
|         ]), | ||||
|         exec_state, | ||||
|     )?; | ||||
|  | ||||
| @ -456,6 +456,10 @@ impl TypeDeclaration { | ||||
|             } | ||||
|             arg_str.push(')'); | ||||
|         } | ||||
|         if let Some(alias) = &self.alias { | ||||
|             arg_str.push_str(" = "); | ||||
|             arg_str.push_str(&alias.to_string()); | ||||
|         } | ||||
|         format!("{}type {}{}", vis, self.name.name, arg_str) | ||||
|     } | ||||
| } | ||||
| @ -811,7 +815,7 @@ impl FunctionExpression { | ||||
|         let tab0 = options.get_indentation(indentation_level); | ||||
|         let tab1 = options.get_indentation(indentation_level + 1); | ||||
|         let return_type = match &self.return_type { | ||||
|             Some(rt) => format!(": {}", rt.to_string()), | ||||
|             Some(rt) => format!(": {rt}"), | ||||
|             None => String::new(), | ||||
|         }; | ||||
|         let body = self.body.recast(&new_options, indentation_level + 1); | ||||
| @ -2427,6 +2431,7 @@ thickness = sqrt(distance * p * FOS * 6 / (sigmaAllow * width))"#; | ||||
| // A comment | ||||
| @(impl = primitive) | ||||
| export type bar(unit, baz) | ||||
| type baz = Foo | Bar | ||||
| "#; | ||||
|         let program = crate::parsing::top_level_parse(some_program_string).unwrap(); | ||||
|         let recasted = program.recast(&Default::default(), 0); | ||||
|  | ||||
		Reference in New Issue
	
	Block a user