Compare commits
	
		
			1 Commits
		
	
	
		
			achalmers/
			...
			achalmers/
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 36a5461de5 | 
| @ -1,36 +1,24 @@ | |||||||
| use crate::{ExecutionError, Value}; | use crate::{ExecutionError, NumericValue, Value}; | ||||||
|  |  | ||||||
| /// Types that can be written to or read from KCEP program memory, | /// Types that can be written to or read from KCEP program memory, | ||||||
| /// but require multiple values to store. | /// but require multiple values to store. | ||||||
| /// They get laid out into multiple consecutive memory addresses. | /// They get laid out into multiple consecutive memory addresses. | ||||||
| pub trait Composite: Sized { | pub trait Composite<const SIZE: usize>: Sized { | ||||||
|     /// How many memory addresses are required to store this value? |  | ||||||
|     const SIZE: usize; |  | ||||||
|     /// Store the value in memory. |     /// Store the value in memory. | ||||||
|     fn into_parts(self) -> Vec<Value>; |     fn into_parts(self) -> [Value; SIZE]; | ||||||
|     /// Read the value from memory. |     /// Read the value from memory. | ||||||
|     fn from_parts(values: Vec<Value>) -> Result<Self, ExecutionError>; |     fn from_parts(values: [Value; SIZE]) -> Result<Self, ExecutionError>; | ||||||
| } | } | ||||||
|  |  | ||||||
| impl Composite for kittycad::types::Point3D { | impl Composite<3> for kittycad::types::Point3D { | ||||||
|     fn into_parts(self) -> Vec<Value> { |     fn into_parts(self) -> [Value; 3] { | ||||||
|         let points = [self.x, self.y, self.z]; |         [self.x, self.y, self.z] | ||||||
|         points |             .map(NumericValue::Float) | ||||||
|             .into_iter() |             .map(Value::NumericValue) | ||||||
|             .map(|x| Value::NumericValue(crate::NumericValue::Float(x))) |  | ||||||
|             .collect() |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     const SIZE: usize = 3; |     fn from_parts(values: [Value; 3]) -> Result<Self, ExecutionError> { | ||||||
|  |         let [x, y, z] = values; | ||||||
|     fn from_parts(values: Vec<Value>) -> Result<Self, ExecutionError> { |  | ||||||
|         let n = values.len(); |  | ||||||
|         let Ok([x, y, z]): Result<[Value; 3], _> = values.try_into() else { |  | ||||||
|             return Err(ExecutionError::MemoryWrongSize { |  | ||||||
|                 actual: n, |  | ||||||
|                 expected: Self::SIZE, |  | ||||||
|             }); |  | ||||||
|         }; |  | ||||||
|         let x = x.try_into()?; |         let x = x.try_into()?; | ||||||
|         let y = y.try_into()?; |         let y = y.try_into()?; | ||||||
|         let z = z.try_into()?; |         let z = z.try_into()?; | ||||||
|  | |||||||
| @ -6,9 +6,10 @@ | |||||||
| //! You can think of it as a domain-specific language for making KittyCAD API calls and using | //! You can think of it as a domain-specific language for making KittyCAD API calls and using | ||||||
| //! the results to make other API calls. | //! the results to make other API calls. | ||||||
|  |  | ||||||
|  | use std::{collections::HashMap, fmt}; | ||||||
|  |  | ||||||
| use composite::Composite; | use composite::Composite; | ||||||
| use serde::{Deserialize, Serialize}; | use serde::{Deserialize, Serialize}; | ||||||
| use std::{collections::HashMap, fmt}; |  | ||||||
|  |  | ||||||
| mod composite; | mod composite; | ||||||
| #[cfg(test)] | #[cfg(test)] | ||||||
| @ -48,7 +49,7 @@ impl Memory { | |||||||
|  |  | ||||||
|     /// Store a composite value (i.e. a value which takes up multiple addresses in memory). |     /// Store a composite value (i.e. a value which takes up multiple addresses in memory). | ||||||
|     /// Store its parts in consecutive memory addresses starting at `start`. |     /// Store its parts in consecutive memory addresses starting at `start`. | ||||||
|     pub fn set_composite<T: Composite>(&mut self, composite_value: T, start: Address) { |     pub fn set_composite<T: Composite<{ N }>, const N: usize>(&mut self, composite_value: T, start: Address) { | ||||||
|         let parts = composite_value.into_parts().into_iter(); |         let parts = composite_value.into_parts().into_iter(); | ||||||
|         for (value, addr) in parts.zip(start.0..) { |         for (value, addr) in parts.zip(start.0..) { | ||||||
|             self.0.insert(addr, value); |             self.0.insert(addr, value); | ||||||
| @ -57,17 +58,14 @@ impl Memory { | |||||||
|  |  | ||||||
|     /// Get a composite value (i.e. a value which takes up multiple addresses in memory). |     /// Get a composite value (i.e. a value which takes up multiple addresses in memory). | ||||||
|     /// Its parts are stored in consecutive memory addresses starting at `start`. |     /// Its parts are stored in consecutive memory addresses starting at `start`. | ||||||
|     pub fn get_composite<T: Composite>(&self, start: Address) -> Result<T, ExecutionError> { |     pub fn get_composite<T: Composite<{ N }>, const N: usize>(&self, start: Address) -> Result<T, ExecutionError> { | ||||||
|         let addrs = start.0..start.0 + T::SIZE; |         let addrs: [Address; N] = core::array::from_fn(|i| Address(i + start.0)); | ||||||
|         let values: Vec<Value> = addrs |         let values: [Value; N] = arr_res_to_res_array(addrs.map(|addr| { | ||||||
|             .into_iter() |             self.get(&addr) | ||||||
|             .map(|a| { |                 .map(|x| x.to_owned()) | ||||||
|                 let addr = Address(a); |                 .ok_or(ExecutionError::MemoryEmpty { addr }) | ||||||
|                 self.get(&addr) |         }))?; | ||||||
|                     .map(|x| x.to_owned()) |  | ||||||
|                     .ok_or(ExecutionError::MemoryEmpty { addr }) |  | ||||||
|             }) |  | ||||||
|             .collect::<Result<_, _>>()?; |  | ||||||
|         T::from_parts(values) |         T::from_parts(values) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -273,6 +271,18 @@ pub enum ExecutionError { | |||||||
|     CannotApplyOperation { op: Operation, operands: Vec<Value> }, |     CannotApplyOperation { op: Operation, operands: Vec<Value> }, | ||||||
|     #[error("Tried to read a '{expected}' from KCEP program memory, found an '{actual}' instead")] |     #[error("Tried to read a '{expected}' from KCEP program memory, found an '{actual}' instead")] | ||||||
|     MemoryWrongType { expected: &'static str, actual: String }, |     MemoryWrongType { expected: &'static str, actual: String }, | ||||||
|     #[error("Wrong size of memory trying to read value from KCEP program memory: got {actual} but wanted {expected}")] | } | ||||||
|     MemoryWrongSize { expected: usize, actual: usize }, |  | ||||||
|  | /// Take an array of result and return a result of array. | ||||||
|  | /// If all members of the array are Ok(T), returns Ok with an array of the T values. | ||||||
|  | /// If any member of the array was Err(E), return Err with the first E value. | ||||||
|  | fn arr_res_to_res_array<T, E, const N: usize>(arr: [Result<T, E>; N]) -> Result<[T; N], E> { | ||||||
|  |     let mut out = core::array::from_fn(|_| None); | ||||||
|  |     for (i, res) in arr.into_iter().enumerate() { | ||||||
|  |         out[i] = match res { | ||||||
|  |             Ok(x) => Some(x), | ||||||
|  |             Err(e) => return Err(e), | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  |     Ok(out.map(|opt| opt.unwrap())) | ||||||
| } | } | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	