Compare commits
	
		
			1 Commits
		
	
	
		
			nightly-v2
			...
			jtran/y-co
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| b4fb903bd0 | 
| @ -2880,6 +2880,30 @@ impl BinaryExpression { | ||||
|         pipe_info: &PipeInfo, | ||||
|         ctx: &ExecutorContext, | ||||
|     ) -> Result<MemoryItem, KclError> { | ||||
|         // First check if we are doing short-circuiting logical operator. | ||||
|         if self.operator == BinaryOperator::LogicalOr { | ||||
|             let left_json_value = self.left.get_result(memory, pipe_info, ctx).await?.get_json_value()?; | ||||
|             let left = json_to_bool(&left_json_value); | ||||
|             if left { | ||||
|                 // Short-circuit. | ||||
|                 return Ok(MemoryItem::UserVal(UserVal { | ||||
|                     value: serde_json::Value::Bool(left), | ||||
|                     meta: vec![Metadata { | ||||
|                         source_range: self.into(), | ||||
|                     }], | ||||
|                 })); | ||||
|             } | ||||
|  | ||||
|             let right_json_value = self.right.get_result(memory, pipe_info, ctx).await?.get_json_value()?; | ||||
|             let right = json_to_bool(&right_json_value); | ||||
|             return Ok(MemoryItem::UserVal(UserVal { | ||||
|                 value: serde_json::Value::Bool(right), | ||||
|                 meta: vec![Metadata { | ||||
|                     source_range: self.into(), | ||||
|                 }], | ||||
|             })); | ||||
|         } | ||||
|  | ||||
|         let left_json_value = self.left.get_result(memory, pipe_info, ctx).await?.get_json_value()?; | ||||
|         let right_json_value = self.right.get_result(memory, pipe_info, ctx).await?.get_json_value()?; | ||||
|  | ||||
| @ -2909,6 +2933,9 @@ impl BinaryExpression { | ||||
|             BinaryOperator::Div => (left / right).into(), | ||||
|             BinaryOperator::Mod => (left % right).into(), | ||||
|             BinaryOperator::Pow => (left.powf(right)).into(), | ||||
|             BinaryOperator::LogicalOr => { | ||||
|                 unreachable!("LogicalOr should have been handled above") | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         Ok(MemoryItem::UserVal(UserVal { | ||||
| @ -2950,6 +2977,27 @@ pub fn parse_json_value_as_string(j: &serde_json::Value) -> Option<String> { | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub fn json_to_bool(j: &serde_json::Value) -> bool { | ||||
|     match j { | ||||
|         JValue::Null => false, | ||||
|         JValue::Bool(b) => *b, | ||||
|         JValue::Number(n) => { | ||||
|             if let Some(n) = n.as_u64() { | ||||
|                 n != 0 | ||||
|             } else if let Some(n) = n.as_i64() { | ||||
|                 n != 0 | ||||
|             } else if let Some(x) = n.as_f64() { | ||||
|                 x != 0.0 && !x.is_nan() | ||||
|             } else { | ||||
|                 false | ||||
|             } | ||||
|         } | ||||
|         JValue::String(s) => !s.is_empty(), | ||||
|         JValue::Array(a) => !a.is_empty(), | ||||
|         JValue::Object(_) => false, | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, FromStr, Display, Bake)] | ||||
| #[databake(path = kcl_lib::ast::types)] | ||||
| #[ts(export)] | ||||
| @ -2980,6 +3028,10 @@ pub enum BinaryOperator { | ||||
|     #[serde(rename = "^")] | ||||
|     #[display("^")] | ||||
|     Pow, | ||||
|     /// Logical OR. | ||||
|     #[serde(rename = "||")] | ||||
|     #[display("||")] | ||||
|     LogicalOr, | ||||
| } | ||||
|  | ||||
| /// Mathematical associativity. | ||||
| @ -3008,6 +3060,7 @@ impl BinaryOperator { | ||||
|             BinaryOperator::Div => *b"div", | ||||
|             BinaryOperator::Mod => *b"mod", | ||||
|             BinaryOperator::Pow => *b"pow", | ||||
|             BinaryOperator::LogicalOr => *b"lor", | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @ -3018,6 +3071,7 @@ impl BinaryOperator { | ||||
|             BinaryOperator::Add | BinaryOperator::Sub => 11, | ||||
|             BinaryOperator::Mul | BinaryOperator::Div | BinaryOperator::Mod => 12, | ||||
|             BinaryOperator::Pow => 6, | ||||
|             BinaryOperator::LogicalOr => 3, | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @ -3025,7 +3079,7 @@ impl BinaryOperator { | ||||
|     /// Taken from <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_precedence#table> | ||||
|     pub fn associativity(&self) -> Associativity { | ||||
|         match self { | ||||
|             Self::Add | Self::Sub | Self::Mul | Self::Div | Self::Mod => Associativity::Left, | ||||
|             Self::Add | Self::Sub | Self::Mul | Self::Div | Self::Mod | Self::LogicalOr => Associativity::Left, | ||||
|             Self::Pow => Associativity::Right, | ||||
|         } | ||||
|     } | ||||
| @ -3089,6 +3143,21 @@ impl UnaryExpression { | ||||
|         pipe_info: &PipeInfo, | ||||
|         ctx: &ExecutorContext, | ||||
|     ) -> Result<MemoryItem, KclError> { | ||||
|         if self.operator == UnaryOperator::Not { | ||||
|             let value = self | ||||
|                 .argument | ||||
|                 .get_result(memory, pipe_info, ctx) | ||||
|                 .await? | ||||
|                 .get_json_value()?; | ||||
|             let negated = !json_to_bool(&value); | ||||
|             return Ok(MemoryItem::UserVal(UserVal { | ||||
|                 value: serde_json::Value::Bool(negated), | ||||
|                 meta: vec![Metadata { | ||||
|                     source_range: self.into(), | ||||
|                 }], | ||||
|             })); | ||||
|         } | ||||
|  | ||||
|         let num = parse_json_number_as_f64( | ||||
|             &self | ||||
|                 .argument | ||||
|  | ||||
| @ -2513,6 +2513,57 @@ let shape = layer() |> patternTransform(10, transform, %)"#; | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     #[tokio::test(flavor = "multi_thread")] | ||||
|     async fn test_execute_ycombinator_is_even() { | ||||
|         let ast = r#" | ||||
| // Heavily inspired by: https://raganwald.com/2018/09/10/why-y.html | ||||
| fn why = (f) => { | ||||
|   fn inner = (maker) => { | ||||
|     fn inner2 = (x) => { | ||||
|       return f(maker(maker), x) | ||||
|     } | ||||
|     return inner2 | ||||
|   } | ||||
|  | ||||
|   return inner( | ||||
|     (maker) => { | ||||
|       fn inner2 = (x) => { | ||||
|         return f(maker(maker), x) | ||||
|       } | ||||
|       return inner2 | ||||
|     } | ||||
|   ) | ||||
| } | ||||
|  | ||||
| fn innerIsEven = (self, n) => { | ||||
|   return !n || !self(n - 1) | ||||
| } | ||||
|  | ||||
| const isEven = why(innerIsEven) | ||||
|  | ||||
| const two = isEven(2) | ||||
| const three = isEven(3) | ||||
| "#; | ||||
|  | ||||
|         let memory = parse_execute(ast).await.unwrap(); | ||||
|         assert_eq!( | ||||
|             serde_json::json!(true), | ||||
|             memory | ||||
|                 .get("two", SourceRange::default()) | ||||
|                 .unwrap() | ||||
|                 .get_json_value() | ||||
|                 .unwrap() | ||||
|         ); | ||||
|         assert_eq!( | ||||
|             serde_json::json!(false), | ||||
|             memory | ||||
|                 .get("three", SourceRange::default()) | ||||
|                 .unwrap() | ||||
|                 .get_json_value() | ||||
|                 .unwrap() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     #[tokio::test(flavor = "multi_thread")] | ||||
|     async fn test_math_execute_with_functions() { | ||||
|         let ast = r#"const myVar = 2 + min(100, -1 + legLen(5, 3))"#; | ||||
|  | ||||
| @ -299,6 +299,7 @@ fn binary_operator(i: TokenSlice) -> PResult<BinaryOperator> { | ||||
|             "*" => BinaryOperator::Mul, | ||||
|             "%" => BinaryOperator::Mod, | ||||
|             "^" => BinaryOperator::Pow, | ||||
|             "||" => BinaryOperator::LogicalOr, | ||||
|             _ => { | ||||
|                 return Err(KclError::Syntax(KclErrorDetails { | ||||
|                     source_ranges: token.as_source_ranges(), | ||||
| @ -1136,11 +1137,11 @@ fn unary_expression(i: TokenSlice) -> PResult<UnaryExpression> { | ||||
|     let (operator, op_token) = any | ||||
|         .try_map(|token: Token| match token.token_type { | ||||
|             TokenType::Operator if token.value == "-" => Ok((UnaryOperator::Neg, token)), | ||||
|             // TODO: negation. Original parser doesn't support `not` yet. | ||||
|             TokenType::Operator => Err(KclError::Syntax(KclErrorDetails { | ||||
|                 source_ranges: token.as_source_ranges(), | ||||
|                 message: format!("{EXPECTED} but found {} which is an operator, but not a unary one (unary operators apply to just a single operand, your operator applies to two or more operands)", token.value.as_str(),), | ||||
|             })), | ||||
|             TokenType::Bang => Ok((UnaryOperator::Not, token)), | ||||
|             other => Err(KclError::Syntax(KclErrorDetails { source_ranges: token.as_source_ranges(), message: format!("{EXPECTED} but found {} which is {}", token.value.as_str(), other,) })), | ||||
|         }) | ||||
|         .context(expected("a unary expression, e.g. -x or -3")) | ||||
|  | ||||
| @ -79,7 +79,7 @@ impl From<ParseError<&[Token], ContextError>> for KclError { | ||||
|         // See https://github.com/KittyCAD/modeling-app/issues/784 | ||||
|         KclError::Syntax(KclErrorDetails { | ||||
|             source_ranges: bad_token.as_source_ranges(), | ||||
|             message: "Unexpected token".to_string(), | ||||
|             message: format!("Unexpected token: {}", bad_token.value), | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -90,7 +90,7 @@ fn word(i: &mut Located<&str>) -> PResult<Token> { | ||||
|  | ||||
| fn operator(i: &mut Located<&str>) -> PResult<Token> { | ||||
|     let (value, range) = alt(( | ||||
|         ">=", "<=", "==", "=>", "!= ", "|>", "*", "+", "-", "/", "%", "=", "<", ">", r"\", "|", "^", | ||||
|         ">=", "<=", "==", "=>", "!= ", "|>", "*", "+", "-", "/", "%", "=", "<", ">", r"\", "||", "|", "^", | ||||
|     )) | ||||
|     .with_span() | ||||
|     .parse_next(i)?; | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	