diff --git a/src/wasm-lib/kcl/src/ast/types/execute.rs b/src/wasm-lib/kcl/src/ast/types/execute.rs index cc365df422..36a7804e2b 100644 --- a/src/wasm-lib/kcl/src/ast/types/execute.rs +++ b/src/wasm-lib/kcl/src/ast/types/execute.rs @@ -42,26 +42,20 @@ impl Node { } }; - let array_json = array.get_json_value()?; - - if let serde_json::Value::Array(array) = array_json { - if let Some(value) = array.get(index) { - Ok(KclValue::UserVal(UserVal { - value: value.clone(), - meta: vec![Metadata { - source_range: self.into(), - }], - })) - } else { - Err(KclError::UndefinedValue(KclErrorDetails { - message: format!("index {} not found in array", index), - source_ranges: vec![self.clone().into()], - })) - } - } else { - Err(KclError::Semantic(KclErrorDetails { + // let array_json = array.get_json_value()?; + let KclValue::Array { value: array, meta: _ } = array else { + return Err(KclError::Semantic(KclErrorDetails { message: format!("MemberExpression array is not an array: {:?}", array), source_ranges: vec![self.clone().into()], + })); + }; + + if let Some(value) = array.get(index) { + Ok(value.to_owned()) + } else { + Err(KclError::UndefinedValue(KclErrorDetails { + message: format!("index {} not found in array", index), + source_ranges: vec![self.clone().into()], })) } } @@ -77,18 +71,11 @@ impl Node { } }; - let object_json = object.get_json_value()?; - // Check the property and object match -- e.g. ints for arrays, strs for objects. - match (object_json, property) { - (JValue::Object(map), Property::String(property)) => { + match (object, property) { + (KclValue::Object { value: map, meta }, Property::String(property)) => { if let Some(value) = map.get(&property) { - Ok(KclValue::UserVal(UserVal { - value: value.clone(), - meta: vec![Metadata { - source_range: self.into(), - }], - })) + Ok(value.to_owned()) } else { Err(KclError::UndefinedValue(KclErrorDetails { message: format!("Property '{property}' not found in object"), @@ -96,22 +83,17 @@ impl Node { })) } } - (JValue::Object(_), p) => Err(KclError::Semantic(KclErrorDetails { + (KclValue::Object { .. }, p) => Err(KclError::Semantic(KclErrorDetails { message: format!( "Only strings can be used as the property of an object, but you're using a {}", p.type_name() ), source_ranges: vec![self.clone().into()], })), - (JValue::Array(arr), Property::Number(index)) => { - let value_of_arr: Option<&JValue> = arr.get(index); + (KclValue::Array { value: arr, meta }, Property::Number(index)) => { + let value_of_arr = arr.get(index); if let Some(value) = value_of_arr { - Ok(KclValue::UserVal(UserVal { - value: value.clone(), - meta: vec![Metadata { - source_range: self.into(), - }], - })) + Ok(value.to_owned()) } else { Err(KclError::UndefinedValue(KclErrorDetails { message: format!("The array doesn't have any item at index {index}"), @@ -119,7 +101,7 @@ impl Node { })) } } - (JValue::Array(_), p) => Err(KclError::Semantic(KclErrorDetails { + (KclValue::Array { .. }, p) => Err(KclError::Semantic(KclErrorDetails { message: format!( "Only integers >= 0 can be used as the index of an array, but you're using a {}", p.type_name() @@ -127,7 +109,7 @@ impl Node { source_ranges: vec![self.clone().into()], })), (being_indexed, _) => { - let t = human_friendly_type(&being_indexed); + let t = being_indexed.human_friendly_type(); Err(KclError::Semantic(KclErrorDetails { message: format!("Only arrays and objects can be indexed, but you're trying to index a {t}"), source_ranges: vec![self.clone().into()], @@ -140,81 +122,134 @@ impl Node { impl Node { #[async_recursion] pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result { - let left_json_value = self.left.get_result(exec_state, ctx).await?.get_json_value()?; - let right_json_value = self.right.get_result(exec_state, ctx).await?.get_json_value()?; + let left_value = self.left.get_result(exec_state, ctx).await?; + let right_value = self.right.get_result(exec_state, ctx).await?; // First check if we are doing string concatenation. if self.operator == BinaryOperator::Add { - if let (Some(left), Some(right)) = ( - parse_json_value_as_string(&left_json_value), - parse_json_value_as_string(&right_json_value), - ) { - let value = serde_json::Value::String(format!("{}{}", left, right)); - return Ok(KclValue::UserVal(UserVal { - value, - meta: vec![Metadata { - source_range: self.into(), - }], - })); + if let ( + KclValue::String { + value: left, + meta: meta_l, + }, + KclValue::String { + value: right, + meta: meta_r, + }, + ) = (left_value, right_value) + { + let mut meta = meta_l; + meta_l.extend(meta_r); + return Ok(KclValue::String { + value: format!("{}{}", left, right), + meta, + }); } } - let left = parse_json_number_as_f64(&left_json_value, self.left.clone().into())?; - let right = parse_json_number_as_f64(&right_json_value, self.right.clone().into())?; - - let value: serde_json::Value = match self.operator { - BinaryOperator::Add => (left + right).into(), - BinaryOperator::Sub => (left - right).into(), - BinaryOperator::Mul => (left * right).into(), - BinaryOperator::Div => (left / right).into(), - BinaryOperator::Mod => (left % right).into(), - BinaryOperator::Pow => (left.powf(right)).into(), - BinaryOperator::Eq => (left == right).into(), - BinaryOperator::Neq => (left != right).into(), - BinaryOperator::Gt => (left > right).into(), - BinaryOperator::Gte => (left >= right).into(), - BinaryOperator::Lt => (left < right).into(), - BinaryOperator::Lte => (left <= right).into(), + let left = parse_number_as_f64(&left_value, self.left.clone().into())?; + let right = parse_number_as_f64(&right_value, self.right.clone().into())?; + + let mut meta = self.left.into(); + meta.extend(self.right.into()); + let value: f64 = match self.operator { + BinaryOperator::Add => KclValue::Number { + value: left + right, + meta, + }, + BinaryOperator::Sub => KclValue::Number { + value: left - right, + meta, + }, + BinaryOperator::Mul => KclValue::Number { + value: left * right, + meta, + }, + BinaryOperator::Div => KclValue::Number { + value: left / right, + meta, + }, + BinaryOperator::Mod => KclValue::Number { + value: left % right, + meta, + }, + BinaryOperator::Pow => KclValue::Number { + value: left.powf(right), + meta, + }, + BinaryOperator::Neq => KclValue::Bool { + value: left != right, + meta, + }, + BinaryOperator::Gt => KclValue::Bool { + value: left > right, + meta, + }, + BinaryOperator::Gte => KclValue::Bool { + value: left >= right, + meta, + }, + BinaryOperator::Lt => KclValue::Bool { + value: left < right, + meta, + }, + BinaryOperator::Lte => KclValue::Bool { + value: left <= right, + meta, + }, + BinaryOperator::Eq => KclValue::Bool { + value: left == right, + meta, + }, }; - Ok(KclValue::UserVal(UserVal { - value, - meta: vec![Metadata { - source_range: self.into(), - }], - })) + Ok(value) } } impl Node { pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result { if self.operator == UnaryOperator::Not { - let value = self.argument.get_result(exec_state, ctx).await?.get_json_value()?; - let Some(bool_value) = json_as_bool(&value) else { + let value = self.argument.get_result(exec_state, ctx).await?; + let KclValue::Bool { + value: bool_value, + meta: _, + } = value + else { return Err(KclError::Semantic(KclErrorDetails { - message: format!("Cannot apply unary operator ! to non-boolean value: {}", value), + message: format!( + "Cannot apply unary operator ! to non-boolean value: {}", + value.human_friendly_type() + ), source_ranges: vec![self.into()], })); }; - let negated = !bool_value; - return Ok(KclValue::UserVal(UserVal { - value: serde_json::Value::Bool(negated), - meta: vec![Metadata { - source_range: self.into(), - }], - })); + let meta = vec![Metadata { + source_range: self.into(), + }]; + let negated = KclValue::Bool { + value: !bool_value, + meta, + }; + + return Ok(negated); } - let num = parse_json_number_as_f64( - &self.argument.get_result(exec_state, ctx).await?.get_json_value()?, - self.into(), - )?; - Ok(KclValue::UserVal(UserVal { - value: (-(num)).into(), - meta: vec![Metadata { - source_range: self.into(), - }], - })) + let value = &self.argument.get_result(exec_state, ctx).await?; + let KclValue::Number { value: num, meta: _ } = value else { + return Err(KclError::Semantic(KclErrorDetails { + message: format!( + "You can only negate numbers, but this is a {}", + value.human_friendly_type() + ), + source_ranges: vec![self.into()], + })); + }; + + let meta = vec![Metadata { + source_range: self.into(), + }]; + Ok(KclValue::Number { value: -num, meta }) } } @@ -616,18 +651,13 @@ fn parse_json_number_as_i64(j: &serde_json::Value, source_range: SourceRange) -> } } -pub fn parse_json_number_as_f64(j: &serde_json::Value, source_range: SourceRange) -> Result { - if let serde_json::Value::Number(n) = &j { - n.as_f64().ok_or_else(|| { - KclError::Syntax(KclErrorDetails { - source_ranges: vec![source_range], - message: format!("Invalid number: {}", j), - }) - }) +pub fn parse_number_as_f64(v: &KclValue, source_range: SourceRange) -> Result { + if let KclValue::Number { value: n, .. } = &v { + Ok(*n) } else { Err(KclError::Syntax(KclErrorDetails { source_ranges: vec![source_range], - message: format!("Invalid number: {}", j), + message: format!("Invalid number: {}", v), })) } } diff --git a/src/wasm-lib/kcl/src/executor.rs b/src/wasm-lib/kcl/src/executor.rs index ddf8ccdcb7..102f91b92a 100644 --- a/src/wasm-lib/kcl/src/executor.rs +++ b/src/wasm-lib/kcl/src/executor.rs @@ -201,33 +201,18 @@ impl Environment { Self { // Prelude bindings: HashMap::from([ - ( - "ZERO".to_string(), - KclValue::UserVal(UserVal { - value: serde_json::Value::Number(serde_json::value::Number::from(0)), - meta: Default::default(), - }), - ), + ("ZERO".to_string(), KclValue::from_number(0.0, Default::default())), ( "QUARTER_TURN".to_string(), - KclValue::UserVal(UserVal { - value: serde_json::Value::Number(serde_json::value::Number::from(90)), - meta: Default::default(), - }), + KclValue::from_number(90.0, Default::default()), ), ( "HALF_TURN".to_string(), - KclValue::UserVal(UserVal { - value: serde_json::Value::Number(serde_json::value::Number::from(180)), - meta: Default::default(), - }), + KclValue::from_number(180.0, Default::default()), ), ( "THREE_QUARTER_TURN".to_string(), - KclValue::UserVal(UserVal { - value: serde_json::Value::Number(serde_json::value::Number::from(270)), - meta: Default::default(), - }), + KclValue::from_number(270.0, Default::default()), ), ]), parent: None, @@ -264,22 +249,15 @@ impl Environment { } for (_, val) in self.bindings.iter_mut() { - let KclValue::UserVal(v) = val else { continue }; - let meta = v.meta.clone(); - let maybe_sg: Result = serde_json::from_value(v.value.clone()); - let Ok(mut sketch) = maybe_sg else { - continue; - }; + let KclValue::Sketch(v) = val else { continue }; + let mut sketch = v.to_owned(); if sketch.original_id == sg.original_id { for tag in sg.tags.iter() { sketch.tags.insert(tag.0.clone(), tag.1.clone()); } } - *val = KclValue::UserVal(UserVal { - meta, - value: serde_json::to_value(sketch).expect("can always turn Sketch into JSON"), - }); + *val = KclValue::Sketch(sketch); } } } @@ -360,7 +338,6 @@ impl IdGenerator { #[ts(export)] #[serde(tag = "type")] pub enum KclValue { - UserVal(UserVal), Bool { value: bool, meta: Vec, @@ -408,6 +385,7 @@ pub enum KclValue { #[serde(rename = "__meta")] meta: Vec, }, + KclNone, } impl KclValue { @@ -415,21 +393,6 @@ impl KclValue { match self { KclValue::Solid(e) => Ok(SolidSet::Solid(e.clone())), KclValue::Solids { value } => Ok(SolidSet::Solids(value.clone())), - KclValue::UserVal(value) => { - let value = value.value.clone(); - match value { - JValue::Null | JValue::Bool(_) | JValue::Number(_) | JValue::String(_) => Err(anyhow::anyhow!( - "Failed to deserialize solid set from JSON {}", - human_friendly_type(&value) - )), - JValue::Array(_) => serde_json::from_value::>>(value) - .map(SolidSet::from) - .map_err(|e| anyhow::anyhow!("Failed to deserialize array of solids from JSON: {}", e)), - JValue::Object(_) => serde_json::from_value::>(value) - .map(SolidSet::from) - .map_err(|e| anyhow::anyhow!("Failed to deserialize solid from JSON: {}", e)), - } - } _ => anyhow::bail!("Not a solid or solids: {:?}", self), } } @@ -438,7 +401,6 @@ impl KclValue { /// on for program logic. pub(crate) fn human_friendly_type(&self) -> &'static str { match self { - KclValue::UserVal(u) => human_friendly_type(&u.value), KclValue::TagDeclarator(_) => "TagDeclarator", KclValue::TagIdentifier(_) => "TagIdentifier", KclValue::Solid(_) => "Solid", @@ -455,6 +417,7 @@ impl KclValue { KclValue::String { .. } => "string", KclValue::Array { .. } => "array", KclValue::Object { .. } => "object", + KclValue::KclNone => "None", } } @@ -910,7 +873,6 @@ pub type MemoryFunction = impl From for Vec { fn from(item: KclValue) -> Self { match item { - KclValue::UserVal(u) => to_vec_sr(&u.meta), KclValue::TagDeclarator(t) => vec![SourceRange([t.start, t.end])], KclValue::TagIdentifier(t) => to_vec_sr(&t.meta), KclValue::Solid(e) => to_vec_sr(&e.meta), @@ -938,7 +900,6 @@ fn to_vec_sr(meta: &[Metadata]) -> Vec { impl From<&KclValue> for Vec { fn from(item: &KclValue) -> Self { match item { - KclValue::UserVal(u) => to_vec_sr(&u.meta), KclValue::TagDeclarator(t) => vec![SourceRange([t.start, t.end])], KclValue::TagIdentifier(t) => to_vec_sr(&t.meta), KclValue::Solid(e) => to_vec_sr(&e.meta), @@ -960,72 +921,33 @@ impl From<&KclValue> for Vec { } impl KclValue { - pub fn get_json_value(&self) -> Result { - if let KclValue::UserVal(user_val) = self { - Ok(user_val.value.clone()) - } else { - serde_json::to_value(self).map_err(|err| { - KclError::Semantic(KclErrorDetails { - message: format!("Cannot convert memory item to json value: {:?}", err), - source_ranges: self.clone().into(), - }) - }) - } - } - - /// Get a JSON value and deserialize it into some concrete type. - pub fn get_json(&self) -> Result { - let json = self.get_json_value()?; - - serde_json::from_value(json).map_err(|e| { - KclError::Type(KclErrorDetails { - message: format!("Failed to deserialize struct from JSON: {}", e), - source_ranges: self.clone().into(), - }) - }) + /// Put the number into a KCL value. + pub fn from_number(f: f64, meta: Vec) -> Self { + Self::Number { value: f, meta } } - /// Get a JSON value and deserialize it into some concrete type. - /// If it's a KCL None, return None. Otherwise return Some. - pub fn get_json_opt(&self) -> Result, KclError> { - let json = self.get_json_value()?; - if let JValue::Object(ref o) = json { - if let Some(JValue::String(s)) = o.get("type") { - if s == "KclNone" { - return Ok(None); - } - } - } - - serde_json::from_value(json) - .map_err(|e| { - KclError::Type(KclErrorDetails { - message: format!("Failed to deserialize struct from JSON: {}", e), - source_ranges: self.clone().into(), - }) - }) - .map(Some) - } - - pub fn as_user_val(&self) -> Option<&UserVal> { - if let KclValue::UserVal(x) = self { - Some(x) + pub fn as_int(&self) -> Option { + if let KclValue::Int { value, meta } = &self { + Some(*value) } else { None } } - /// If this value is of type u32, return it. + /// If this value fits in a u32, return it. pub fn get_u32(&self, source_ranges: Vec) -> Result { - let err = KclError::Semantic(KclErrorDetails { - message: "Expected an integer >= 0".to_owned(), - source_ranges, - }); - self.as_user_val() - .and_then(|uv| uv.value.as_number()) - .and_then(|n| n.as_u64()) - .and_then(|n| u32::try_from(n).ok()) - .ok_or(err) + let u = self.as_int().and_then(|n| u64::try_from(n).ok()).ok_or_else(|| { + KclError::Semantic(KclErrorDetails { + message: "Expected an integer >= 0".to_owned(), + source_ranges: source_ranges.clone(), + }) + })?; + u32::try_from(u).map_err(|_| { + KclError::Semantic(KclErrorDetails { + message: "Number was too big".to_owned(), + source_ranges, + }) + }) } /// If this value is of type function, return it. @@ -1050,16 +972,6 @@ impl KclValue { pub fn get_tag_identifier(&self) -> Result { match self { KclValue::TagIdentifier(t) => Ok(*t.clone()), - KclValue::UserVal(_) => { - if let Some(identifier) = self.get_json_opt::()? { - Ok(identifier) - } else { - Err(KclError::Semantic(KclErrorDetails { - message: format!("Not a tag identifier: {:?}", self), - source_ranges: self.clone().into(), - })) - } - } _ => Err(KclError::Semantic(KclErrorDetails { message: format!("Not a tag identifier: {:?}", self), source_ranges: self.clone().into(),