@ -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 => {
|
||||
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