KCL literals have their own type system instead of reusing JSON.
We now distinguish between KCL literals of: - String - f64 - i64 - u64 instead of reusing JSON values as the only KCL literal.
This commit is contained in:
		| @ -440,11 +440,15 @@ export function splitPathAtPipeExpression(pathToNode: PathToNode): { | ||||
| } | ||||
|  | ||||
| export function createLiteral(value: string | number): Literal { | ||||
|   const literalValue = | ||||
|     typeof value === 'string' | ||||
|       ? ({ type: 'string', data: value } as const) | ||||
|       : ({ type: 'fractional', data: value } as const) | ||||
|   return { | ||||
|     type: 'Literal', | ||||
|     start: 0, | ||||
|     end: 0, | ||||
|     value, | ||||
|     value: literalValue, | ||||
|     raw: `${value}`, | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -7,14 +7,18 @@ use parse_display::{Display, FromStr}; | ||||
| use schemars::JsonSchema; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| use serde_json::Map; | ||||
| use serde_json::Value as JValue; | ||||
| use tower_lsp::lsp_types::{CompletionItem, CompletionItemKind, DocumentSymbol, Range as LspRange, SymbolKind}; | ||||
|  | ||||
| pub use self::literal_value::LiteralValue; | ||||
| use crate::{ | ||||
|     errors::{KclError, KclErrorDetails}, | ||||
|     executor::{ExecutorContext, MemoryItem, Metadata, PipeInfo, ProgramMemory, SourceRange, UserVal}, | ||||
|     parser::PIPE_OPERATOR, | ||||
| }; | ||||
|  | ||||
| mod literal_value; | ||||
|  | ||||
| #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)] | ||||
| #[ts(export)] | ||||
| #[serde(rename_all = "camelCase")] | ||||
| @ -1312,24 +1316,18 @@ impl VariableDeclarator { | ||||
| pub struct Literal { | ||||
|     pub start: usize, | ||||
|     pub end: usize, | ||||
|     pub value: serde_json::Value, | ||||
|     pub value: LiteralValue, | ||||
|     pub raw: String, | ||||
| } | ||||
|  | ||||
| impl_value_meta!(Literal); | ||||
|  | ||||
| impl From<Literal> for Value { | ||||
|     fn from(literal: Literal) -> Self { | ||||
|         Value::Literal(Box::new(literal)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Literal { | ||||
|     pub fn new(value: serde_json::Value) -> Self { | ||||
|     pub fn new(value: LiteralValue) -> Self { | ||||
|         Self { | ||||
|             start: 0, | ||||
|             end: 0, | ||||
|             raw: value.to_string(), | ||||
|             raw: JValue::from(value.clone()).to_string(), | ||||
|             value, | ||||
|         } | ||||
|     } | ||||
| @ -1343,11 +1341,17 @@ impl Literal { | ||||
|     } | ||||
|  | ||||
|     fn recast(&self) -> String { | ||||
|         if let serde_json::Value::String(value) = &self.value { | ||||
|             let quote = if self.raw.trim().starts_with('"') { '"' } else { '\'' }; | ||||
|             format!("{}{}{}", quote, value, quote) | ||||
|         } else { | ||||
|             self.value.to_string() | ||||
|         match self.value { | ||||
|             // Use the debug representation, not .to_string(), because | ||||
|             // calling (6.0).to_string() outputs "6" not "6.0". | ||||
|             // It's important that fractional numbers stay fractional after recasting. | ||||
|             LiteralValue::Fractional(n) => format!("{n:?}"), | ||||
|             LiteralValue::UInteger(n) => n.to_string(), | ||||
|             LiteralValue::IInteger(n) => n.to_string(), | ||||
|             LiteralValue::String(ref s) => { | ||||
|                 let quote = if self.raw.trim().starts_with('"') { '"' } else { '\'' }; | ||||
|                 format!("{quote}{s}{quote}") | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1355,7 +1359,7 @@ impl Literal { | ||||
| impl From<Literal> for MemoryItem { | ||||
|     fn from(literal: Literal) -> Self { | ||||
|         MemoryItem::UserVal(UserVal { | ||||
|             value: literal.value.clone(), | ||||
|             value: JValue::from(literal.value.clone()), | ||||
|             meta: vec![Metadata { | ||||
|                 source_range: literal.into(), | ||||
|             }], | ||||
| @ -1366,7 +1370,7 @@ impl From<Literal> for MemoryItem { | ||||
| impl From<&Box<Literal>> for MemoryItem { | ||||
|     fn from(literal: &Box<Literal>) -> Self { | ||||
|         MemoryItem::UserVal(UserVal { | ||||
|             value: literal.value.clone(), | ||||
|             value: JValue::from(literal.value.clone()), | ||||
|             meta: vec![Metadata { | ||||
|                 source_range: literal.into(), | ||||
|             }], | ||||
| @ -1967,17 +1971,22 @@ impl MemberExpression { | ||||
|             LiteralIdentifier::Identifier(identifier) => identifier.name.to_string(), | ||||
|             LiteralIdentifier::Literal(literal) => { | ||||
|                 let value = literal.value.clone(); | ||||
|                 // Parse this as a string. | ||||
|                 if let serde_json::Value::String(string) = value { | ||||
|                     string | ||||
|                 } else if let serde_json::Value::Number(_) = &value { | ||||
|                     // It can also be a number if we are getting a member of an array. | ||||
|                     return self.get_result_array(memory, parse_json_number_as_usize(&value, self.into())?); | ||||
|                 } else { | ||||
|                     return Err(KclError::Semantic(KclErrorDetails { | ||||
|                         message: format!("Expected string literal or number for property name, found {:?}", value), | ||||
|                         source_ranges: vec![literal.into()], | ||||
|                     })); | ||||
|                 match value { | ||||
|                     LiteralValue::UInteger(x) => return self.get_result_array(memory, x as usize), | ||||
|                     LiteralValue::IInteger(x) if x > 0 => return self.get_result_array(memory, x as usize), | ||||
|                     LiteralValue::IInteger(x) => { | ||||
|                         return Err(KclError::Syntax(KclErrorDetails { | ||||
|                             source_ranges: vec![self.into()], | ||||
|                             message: format!("invalid index: {x}"), | ||||
|                         })) | ||||
|                     } | ||||
|                     LiteralValue::Fractional(x) => { | ||||
|                         return Err(KclError::Syntax(KclErrorDetails { | ||||
|                             source_ranges: vec![self.into()], | ||||
|                             message: format!("invalid index: {x}"), | ||||
|                         })) | ||||
|                     } | ||||
|                     LiteralValue::String(s) => s, | ||||
|                 } | ||||
|             } | ||||
|         }; | ||||
| @ -2209,22 +2218,6 @@ pub fn parse_json_number_as_f64(j: &serde_json::Value, source_range: SourceRange | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub fn parse_json_number_as_usize(j: &serde_json::Value, source_range: SourceRange) -> Result<usize, KclError> { | ||||
|     if let serde_json::Value::Number(n) = &j { | ||||
|         Ok(n.as_i64().ok_or_else(|| { | ||||
|             KclError::Syntax(KclErrorDetails { | ||||
|                 source_ranges: vec![source_range], | ||||
|                 message: format!("Invalid index: {}", j), | ||||
|             }) | ||||
|         })? as usize) | ||||
|     } else { | ||||
|         Err(KclError::Syntax(KclErrorDetails { | ||||
|             source_ranges: vec![source_range], | ||||
|             message: format!("Invalid index: {}", j), | ||||
|         })) | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub fn parse_json_value_as_string(j: &serde_json::Value) -> Option<String> { | ||||
|     if let serde_json::Value::String(n) = &j { | ||||
|         Some(n.clone()) | ||||
|  | ||||
							
								
								
									
										77
									
								
								src/wasm-lib/kcl/src/ast/types/literal_value.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								src/wasm-lib/kcl/src/ast/types/literal_value.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,77 @@ | ||||
| use schemars::JsonSchema; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| use serde_json::Value as JValue; | ||||
|  | ||||
| use super::{Literal, Value}; | ||||
|  | ||||
| #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)] | ||||
| #[ts(export)] | ||||
| #[serde(tag = "type", content = "data", rename_all = "snake_case")] | ||||
| pub enum LiteralValue { | ||||
|     UInteger(u64), | ||||
|     IInteger(i64), | ||||
|     Fractional(f64), | ||||
|     String(String), | ||||
| } | ||||
|  | ||||
| impl From<Literal> for Value { | ||||
|     fn from(literal: Literal) -> Self { | ||||
|         Value::Literal(Box::new(literal)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<LiteralValue> for JValue { | ||||
|     fn from(value: LiteralValue) -> Self { | ||||
|         match value { | ||||
|             LiteralValue::IInteger(x) => x.into(), | ||||
|             LiteralValue::UInteger(x) => x.into(), | ||||
|             LiteralValue::Fractional(x) => x.into(), | ||||
|             LiteralValue::String(x) => x.into(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<f64> for LiteralValue { | ||||
|     fn from(value: f64) -> Self { | ||||
|         Self::Fractional(value) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<i64> for LiteralValue { | ||||
|     fn from(value: i64) -> Self { | ||||
|         Self::IInteger(value) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<String> for LiteralValue { | ||||
|     fn from(value: String) -> Self { | ||||
|         Self::String(value) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<u64> for LiteralValue { | ||||
|     fn from(value: u64) -> Self { | ||||
|         Self::UInteger(value) | ||||
|     } | ||||
| } | ||||
| impl From<u32> for LiteralValue { | ||||
|     fn from(value: u32) -> Self { | ||||
|         Self::UInteger(value as u64) | ||||
|     } | ||||
| } | ||||
| impl From<u16> for LiteralValue { | ||||
|     fn from(value: u16) -> Self { | ||||
|         Self::UInteger(value as u64) | ||||
|     } | ||||
| } | ||||
| impl From<u8> for LiteralValue { | ||||
|     fn from(value: u8) -> Self { | ||||
|         Self::UInteger(value as u64) | ||||
|     } | ||||
| } | ||||
| impl From<&'static str> for LiteralValue { | ||||
|     fn from(value: &'static str) -> Self { | ||||
|         // TODO: Make this Cow<str> | ||||
|         Self::String(value.to_owned()) | ||||
|     } | ||||
| } | ||||
| @ -94,7 +94,7 @@ mod tests { | ||||
|     #[test] | ||||
|     fn parse_and_evaluate() { | ||||
|         /// Make a literal | ||||
|         fn lit(n: u8) -> BinaryPart { | ||||
|         fn lit(n: u64) -> BinaryPart { | ||||
|             BinaryPart::Literal(Box::new(Literal { | ||||
|                 start: 0, | ||||
|                 end: 0, | ||||
|  | ||||
| @ -1,4 +1,3 @@ | ||||
| use serde_json::{Number as JNumber, Value as JValue}; | ||||
| use winnow::{ | ||||
|     combinator::{alt, delimited, opt, peek, preceded, repeat, separated0, terminated}, | ||||
|     dispatch, | ||||
| @ -10,10 +9,10 @@ use winnow::{ | ||||
| use crate::{ | ||||
|     ast::types::{ | ||||
|         ArrayExpression, BinaryExpression, BinaryOperator, BinaryPart, BodyItem, CallExpression, CommentStyle, | ||||
|         ExpressionStatement, FunctionExpression, Identifier, Literal, LiteralIdentifier, MemberExpression, | ||||
|         MemberObject, NonCodeMeta, NonCodeNode, NonCodeValue, ObjectExpression, ObjectProperty, PipeExpression, | ||||
|         PipeSubstitution, Program, ReturnStatement, UnaryExpression, UnaryOperator, Value, VariableDeclaration, | ||||
|         VariableDeclarator, VariableKind, | ||||
|         ExpressionStatement, FunctionExpression, Identifier, Literal, LiteralIdentifier, LiteralValue, | ||||
|         MemberExpression, MemberObject, NonCodeMeta, NonCodeNode, NonCodeValue, ObjectExpression, ObjectProperty, | ||||
|         PipeExpression, PipeSubstitution, Program, ReturnStatement, UnaryExpression, UnaryOperator, Value, | ||||
|         VariableDeclaration, VariableDeclarator, VariableKind, | ||||
|     }, | ||||
|     errors::{KclError, KclErrorDetails}, | ||||
|     executor::SourceRange, | ||||
| @ -216,7 +215,7 @@ pub fn string_literal(i: TokenSlice) -> PResult<Literal> { | ||||
|         .try_map(|token: Token| match token.token_type { | ||||
|             TokenType::String => { | ||||
|                 let s = token.value[1..token.value.len() - 1].to_string(); | ||||
|                 Ok((JValue::String(s), token)) | ||||
|                 Ok((LiteralValue::from(s), token)) | ||||
|             } | ||||
|             _ => Err(KclError::Syntax(KclErrorDetails { | ||||
|                 source_ranges: token.as_source_ranges(), | ||||
| @ -238,8 +237,8 @@ fn unsigned_number_literal(i: TokenSlice) -> PResult<Literal> { | ||||
|     let (value, token) = any | ||||
|         .try_map(|token: Token| match token.token_type { | ||||
|             TokenType::Number => { | ||||
|                 if let Ok(x) = token.value.parse::<i64>() { | ||||
|                     return Ok((JValue::Number(JNumber::from(x)), token)); | ||||
|                 if let Ok(x) = token.value.parse::<u64>() { | ||||
|                     return Ok((LiteralValue::UInteger(x), token)); | ||||
|                 } | ||||
|                 let x: f64 = token.value.parse().map_err(|_| { | ||||
|                     KclError::Syntax(KclErrorDetails { | ||||
| @ -248,13 +247,7 @@ fn unsigned_number_literal(i: TokenSlice) -> PResult<Literal> { | ||||
|                     }) | ||||
|                 })?; | ||||
|  | ||||
|                 match JNumber::from_f64(x) { | ||||
|                     Some(n) => Ok((JValue::Number(n), token)), | ||||
|                     None => Err(KclError::Syntax(KclErrorDetails { | ||||
|                         source_ranges: token.as_source_ranges(), | ||||
|                         message: format!("Invalid float: {}", token.value), | ||||
|                     })), | ||||
|                 } | ||||
|                 Ok((LiteralValue::Fractional(x), token)) | ||||
|             } | ||||
|             _ => Err(KclError::Syntax(KclErrorDetails { | ||||
|                 source_ranges: token.as_source_ranges(), | ||||
| @ -407,7 +400,7 @@ fn integer_range(i: TokenSlice) -> PResult<Vec<Value>> { | ||||
|             Value::Literal(Box::new(Literal { | ||||
|                 start: token0.start, | ||||
|                 end: token0.end, | ||||
|                 value: JValue::Number(num.into()), | ||||
|                 value: num.into(), | ||||
|                 raw: num.to_string(), | ||||
|             })) | ||||
|         }) | ||||
| @ -1459,7 +1452,7 @@ const mySk1 = startSketchAt([0, 0])"#; | ||||
|                         argument: Value::Literal(Box::new(Literal { | ||||
|                             start: 32, | ||||
|                             end: 33, | ||||
|                             value: JValue::Number(JNumber::from(2)), | ||||
|                             value: 2u32.into(), | ||||
|                             raw: "2".to_owned(), | ||||
|                         })), | ||||
|                     })], | ||||
| @ -1614,7 +1607,7 @@ const mySk1 = startSketchAt([0, 0])"#; | ||||
|             BinaryPart::Literal(Box::new(Literal { | ||||
|                 start: 9, | ||||
|                 end: 10, | ||||
|                 value: JValue::Number(JNumber::from(3)), | ||||
|                 value: 3u32.into(), | ||||
|                 raw: "3".to_owned(), | ||||
|             })) | ||||
|         ); | ||||
| @ -1774,11 +1767,11 @@ const mySk1 = startSketchAt([0, 0])"#; | ||||
|             let BinaryPart::Literal(left) = actual.left else { | ||||
|                 panic!("should be expression"); | ||||
|             }; | ||||
|             assert_eq!(left.value, serde_json::Value::Number(1.into())); | ||||
|             assert_eq!(left.value, 1u32.into()); | ||||
|             let BinaryPart::Literal(right) = actual.right else { | ||||
|                 panic!("should be expression"); | ||||
|             }; | ||||
|             assert_eq!(right.value, serde_json::Value::Number(2.into())); | ||||
|             assert_eq!(right.value, 2u32.into()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @ -1957,12 +1950,10 @@ const mySk1 = startSketchAt([0, 0])"#; | ||||
|         let parsed_literal = literal.parse(&tokens).unwrap(); | ||||
|         assert_eq!( | ||||
|             parsed_literal.value, | ||||
|             JValue::String( | ||||
|                 " | ||||
|             " | ||||
|            // a comment | ||||
|              " | ||||
|                 .to_owned() | ||||
|             ) | ||||
|             .into() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
| @ -2067,13 +2058,13 @@ const mySk1 = startSketchAt([0, 0])"#; | ||||
|             left: BinaryPart::Literal(Box::new(Literal { | ||||
|                 start: 0, | ||||
|                 end: 1, | ||||
|                 value: serde_json::Value::Number(serde_json::Number::from(5)), | ||||
|                 value: 5u32.into(), | ||||
|                 raw: "5".to_owned(), | ||||
|             })), | ||||
|             right: BinaryPart::Literal(Box::new(Literal { | ||||
|                 start: 4, | ||||
|                 end: 7, | ||||
|                 value: serde_json::Value::String("a".to_owned()), | ||||
|                 value: "a".into(), | ||||
|                 raw: r#""a""#.to_owned(), | ||||
|             })), | ||||
|         }; | ||||
| @ -2180,14 +2171,14 @@ const mySk1 = startSketchAt([0, 0])"#; | ||||
|                     left: BinaryPart::Literal(Box::new(Literal { | ||||
|                         start: 0, | ||||
|                         end: 1, | ||||
|                         value: serde_json::Value::Number(serde_json::Number::from(5)), | ||||
|                         value: 5u32.into(), | ||||
|                         raw: "5".to_string(), | ||||
|                     })), | ||||
|                     operator: BinaryOperator::Add, | ||||
|                     right: BinaryPart::Literal(Box::new(Literal { | ||||
|                         start: 3, | ||||
|                         end: 4, | ||||
|                         value: serde_json::Value::Number(serde_json::Number::from(6)), | ||||
|                         value: 6u32.into(), | ||||
|                         raw: "6".to_string(), | ||||
|                     })), | ||||
|                 })), | ||||
| @ -2466,67 +2457,67 @@ e | ||||
|                             Value::Literal(Box::new(Literal { | ||||
|                                 start: 17, | ||||
|                                 end: 18, | ||||
|                                 value: 0.into(), | ||||
|                                 value: 0u32.into(), | ||||
|                                 raw: "0".to_string(), | ||||
|                             })), | ||||
|                             Value::Literal(Box::new(Literal { | ||||
|                                 start: 17, | ||||
|                                 end: 18, | ||||
|                                 value: 1.into(), | ||||
|                                 value: 1u32.into(), | ||||
|                                 raw: "1".to_string(), | ||||
|                             })), | ||||
|                             Value::Literal(Box::new(Literal { | ||||
|                                 start: 17, | ||||
|                                 end: 18, | ||||
|                                 value: 2.into(), | ||||
|                                 value: 2u32.into(), | ||||
|                                 raw: "2".to_string(), | ||||
|                             })), | ||||
|                             Value::Literal(Box::new(Literal { | ||||
|                                 start: 17, | ||||
|                                 end: 18, | ||||
|                                 value: 3.into(), | ||||
|                                 value: 3u32.into(), | ||||
|                                 raw: "3".to_string(), | ||||
|                             })), | ||||
|                             Value::Literal(Box::new(Literal { | ||||
|                                 start: 17, | ||||
|                                 end: 18, | ||||
|                                 value: 4.into(), | ||||
|                                 value: 4u32.into(), | ||||
|                                 raw: "4".to_string(), | ||||
|                             })), | ||||
|                             Value::Literal(Box::new(Literal { | ||||
|                                 start: 17, | ||||
|                                 end: 18, | ||||
|                                 value: 5.into(), | ||||
|                                 value: 5u32.into(), | ||||
|                                 raw: "5".to_string(), | ||||
|                             })), | ||||
|                             Value::Literal(Box::new(Literal { | ||||
|                                 start: 17, | ||||
|                                 end: 18, | ||||
|                                 value: 6.into(), | ||||
|                                 value: 6u32.into(), | ||||
|                                 raw: "6".to_string(), | ||||
|                             })), | ||||
|                             Value::Literal(Box::new(Literal { | ||||
|                                 start: 17, | ||||
|                                 end: 18, | ||||
|                                 value: 7.into(), | ||||
|                                 value: 7u32.into(), | ||||
|                                 raw: "7".to_string(), | ||||
|                             })), | ||||
|                             Value::Literal(Box::new(Literal { | ||||
|                                 start: 17, | ||||
|                                 end: 18, | ||||
|                                 value: 8.into(), | ||||
|                                 value: 8u32.into(), | ||||
|                                 raw: "8".to_string(), | ||||
|                             })), | ||||
|                             Value::Literal(Box::new(Literal { | ||||
|                                 start: 17, | ||||
|                                 end: 18, | ||||
|                                 value: 9.into(), | ||||
|                                 value: 9u32.into(), | ||||
|                                 raw: "9".to_string(), | ||||
|                             })), | ||||
|                             Value::Literal(Box::new(Literal { | ||||
|                                 start: 17, | ||||
|                                 end: 18, | ||||
|                                 value: 10.into(), | ||||
|                                 value: 10u32.into(), | ||||
|                                 raw: "10".to_string(), | ||||
|                             })), | ||||
|                         ], | ||||
|  | ||||
		Reference in New Issue
	
	Block a user