Unify execution state into a single struct (#3877)
* Add ExecState that combines ProgramMemory and DynamicState * Remove unneeded clones * Add exec_state parameter to all KCL stdlib functions * Move pipe value into ExecState * Add test for pipe substitution not leaking into function calls * KCL: Better message on assertEqual function Also add a new no-visual test for performance testing. * Fix new array module to use ExecState --------- Co-authored-by: Adam Chalmers <adam.chalmers@zoo.dev>
This commit is contained in:
		@ -269,7 +269,7 @@ fn do_stdlib_inner(
 | 
			
		||||
        let ty_string = rust_type_to_openapi_type(&ty_string);
 | 
			
		||||
        let required = !ty_ident.to_string().starts_with("Option <");
 | 
			
		||||
 | 
			
		||||
        if ty_string != "Args" {
 | 
			
		||||
        if ty_string != "ExecState" && ty_string != "Args" {
 | 
			
		||||
            let schema = if ty_ident.to_string().starts_with("Vec < ")
 | 
			
		||||
                || ty_ident.to_string().starts_with("Option <")
 | 
			
		||||
                || ty_ident.to_string().starts_with('[')
 | 
			
		||||
@ -387,11 +387,12 @@ fn do_stdlib_inner(
 | 
			
		||||
        #const_struct
 | 
			
		||||
 | 
			
		||||
        fn #boxed_fn_name_ident(
 | 
			
		||||
            exec_state: &mut crate::executor::ExecState,
 | 
			
		||||
            args: crate::std::Args,
 | 
			
		||||
        ) -> std::pin::Pin<
 | 
			
		||||
            Box<dyn std::future::Future<Output = anyhow::Result<crate::executor::KclValue, crate::errors::KclError>> + Send>,
 | 
			
		||||
            Box<dyn std::future::Future<Output = anyhow::Result<crate::executor::KclValue, crate::errors::KclError>> + Send + '_>,
 | 
			
		||||
        > {
 | 
			
		||||
            Box::pin(#fn_name_ident(args))
 | 
			
		||||
            Box::pin(#fn_name_ident(exec_state, args))
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        impl #docs_crate::StdLibFn for #name_ident
 | 
			
		||||
@ -662,6 +663,9 @@ fn clean_ty_string(t: &str) -> (String, proc_macro2::TokenStream) {
 | 
			
		||||
        .replace("mut", "")
 | 
			
		||||
        .replace("< 'a >", "")
 | 
			
		||||
        .replace(' ', "");
 | 
			
		||||
    if ty_string.starts_with("ExecState") {
 | 
			
		||||
        ty_string = "ExecState".to_string();
 | 
			
		||||
    }
 | 
			
		||||
    if ty_string.starts_with("Args") {
 | 
			
		||||
        ty_string = "Args".to_string();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -85,6 +85,32 @@ fn test_args_with_lifetime() {
 | 
			
		||||
    expectorate::assert_contents("tests/args_with_lifetime.gen", &get_text_fmt(&item).unwrap());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
fn test_args_with_exec_state() {
 | 
			
		||||
    let (item, mut errors) = do_stdlib(
 | 
			
		||||
        quote! {
 | 
			
		||||
            name = "someFunction",
 | 
			
		||||
        },
 | 
			
		||||
        quote! {
 | 
			
		||||
            /// Docs
 | 
			
		||||
            /// ```
 | 
			
		||||
            /// someFunction()
 | 
			
		||||
            /// ```
 | 
			
		||||
            fn inner_some_function<'a>(
 | 
			
		||||
                exec_state: &mut ExecState,
 | 
			
		||||
                args: &Args,
 | 
			
		||||
            ) -> i32 {
 | 
			
		||||
                3
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
    )
 | 
			
		||||
    .unwrap();
 | 
			
		||||
    if let Some(e) = errors.pop() {
 | 
			
		||||
        panic!("{e}");
 | 
			
		||||
    }
 | 
			
		||||
    expectorate::assert_contents("tests/test_args_with_exec_state.gen", &get_text_fmt(&item).unwrap());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
fn test_stdlib_line_to() {
 | 
			
		||||
    let (item, errors) = do_stdlib(
 | 
			
		||||
 | 
			
		||||
@ -44,15 +44,17 @@ pub(crate) struct SomeFn {}
 | 
			
		||||
#[doc = "Std lib function: someFn\nDocs"]
 | 
			
		||||
pub(crate) const SomeFn: SomeFn = SomeFn {};
 | 
			
		||||
fn boxed_someFn(
 | 
			
		||||
    exec_state: &mut crate::executor::ExecState,
 | 
			
		||||
    args: crate::std::Args,
 | 
			
		||||
) -> std::pin::Pin<
 | 
			
		||||
    Box<
 | 
			
		||||
        dyn std::future::Future<
 | 
			
		||||
                Output = anyhow::Result<crate::executor::KclValue, crate::errors::KclError>,
 | 
			
		||||
            > + Send,
 | 
			
		||||
            > + Send
 | 
			
		||||
            + '_,
 | 
			
		||||
    >,
 | 
			
		||||
> {
 | 
			
		||||
    Box::pin(someFn(args))
 | 
			
		||||
    Box::pin(someFn(exec_state, args))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl crate::docs::StdLibFn for SomeFn {
 | 
			
		||||
 | 
			
		||||
@ -44,15 +44,17 @@ pub(crate) struct SomeFn {}
 | 
			
		||||
#[doc = "Std lib function: someFn\nDocs"]
 | 
			
		||||
pub(crate) const SomeFn: SomeFn = SomeFn {};
 | 
			
		||||
fn boxed_someFn(
 | 
			
		||||
    exec_state: &mut crate::executor::ExecState,
 | 
			
		||||
    args: crate::std::Args,
 | 
			
		||||
) -> std::pin::Pin<
 | 
			
		||||
    Box<
 | 
			
		||||
        dyn std::future::Future<
 | 
			
		||||
                Output = anyhow::Result<crate::executor::KclValue, crate::errors::KclError>,
 | 
			
		||||
            > + Send,
 | 
			
		||||
            > + Send
 | 
			
		||||
            + '_,
 | 
			
		||||
    >,
 | 
			
		||||
> {
 | 
			
		||||
    Box::pin(someFn(args))
 | 
			
		||||
    Box::pin(someFn(exec_state, args))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl crate::docs::StdLibFn for SomeFn {
 | 
			
		||||
 | 
			
		||||
@ -77,15 +77,17 @@ pub(crate) struct Show {}
 | 
			
		||||
#[doc = "Std lib function: show\nThis is some function.\nIt does shit."]
 | 
			
		||||
pub(crate) const Show: Show = Show {};
 | 
			
		||||
fn boxed_show(
 | 
			
		||||
    exec_state: &mut crate::executor::ExecState,
 | 
			
		||||
    args: crate::std::Args,
 | 
			
		||||
) -> std::pin::Pin<
 | 
			
		||||
    Box<
 | 
			
		||||
        dyn std::future::Future<
 | 
			
		||||
                Output = anyhow::Result<crate::executor::KclValue, crate::errors::KclError>,
 | 
			
		||||
            > + Send,
 | 
			
		||||
            > + Send
 | 
			
		||||
            + '_,
 | 
			
		||||
    >,
 | 
			
		||||
> {
 | 
			
		||||
    Box::pin(show(args))
 | 
			
		||||
    Box::pin(show(exec_state, args))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl crate::docs::StdLibFn for Show {
 | 
			
		||||
 | 
			
		||||
@ -44,15 +44,17 @@ pub(crate) struct Show {}
 | 
			
		||||
#[doc = "Std lib function: show\nThis is some function.\nIt does shit."]
 | 
			
		||||
pub(crate) const Show: Show = Show {};
 | 
			
		||||
fn boxed_show(
 | 
			
		||||
    exec_state: &mut crate::executor::ExecState,
 | 
			
		||||
    args: crate::std::Args,
 | 
			
		||||
) -> std::pin::Pin<
 | 
			
		||||
    Box<
 | 
			
		||||
        dyn std::future::Future<
 | 
			
		||||
                Output = anyhow::Result<crate::executor::KclValue, crate::errors::KclError>,
 | 
			
		||||
            > + Send,
 | 
			
		||||
            > + Send
 | 
			
		||||
            + '_,
 | 
			
		||||
    >,
 | 
			
		||||
> {
 | 
			
		||||
    Box::pin(show(args))
 | 
			
		||||
    Box::pin(show(exec_state, args))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl crate::docs::StdLibFn for Show {
 | 
			
		||||
 | 
			
		||||
@ -78,15 +78,17 @@ pub(crate) struct MyFunc {}
 | 
			
		||||
#[doc = "Std lib function: myFunc\nThis is some function.\nIt does shit."]
 | 
			
		||||
pub(crate) const MyFunc: MyFunc = MyFunc {};
 | 
			
		||||
fn boxed_my_func(
 | 
			
		||||
    exec_state: &mut crate::executor::ExecState,
 | 
			
		||||
    args: crate::std::Args,
 | 
			
		||||
) -> std::pin::Pin<
 | 
			
		||||
    Box<
 | 
			
		||||
        dyn std::future::Future<
 | 
			
		||||
                Output = anyhow::Result<crate::executor::KclValue, crate::errors::KclError>,
 | 
			
		||||
            > + Send,
 | 
			
		||||
            > + Send
 | 
			
		||||
            + '_,
 | 
			
		||||
    >,
 | 
			
		||||
> {
 | 
			
		||||
    Box::pin(my_func(args))
 | 
			
		||||
    Box::pin(my_func(exec_state, args))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl crate::docs::StdLibFn for MyFunc {
 | 
			
		||||
 | 
			
		||||
@ -78,15 +78,17 @@ pub(crate) struct LineTo {}
 | 
			
		||||
#[doc = "Std lib function: lineTo\nThis is some function.\nIt does shit."]
 | 
			
		||||
pub(crate) const LineTo: LineTo = LineTo {};
 | 
			
		||||
fn boxed_line_to(
 | 
			
		||||
    exec_state: &mut crate::executor::ExecState,
 | 
			
		||||
    args: crate::std::Args,
 | 
			
		||||
) -> std::pin::Pin<
 | 
			
		||||
    Box<
 | 
			
		||||
        dyn std::future::Future<
 | 
			
		||||
                Output = anyhow::Result<crate::executor::KclValue, crate::errors::KclError>,
 | 
			
		||||
            > + Send,
 | 
			
		||||
            > + Send
 | 
			
		||||
            + '_,
 | 
			
		||||
    >,
 | 
			
		||||
> {
 | 
			
		||||
    Box::pin(line_to(args))
 | 
			
		||||
    Box::pin(line_to(exec_state, args))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl crate::docs::StdLibFn for LineTo {
 | 
			
		||||
 | 
			
		||||
@ -77,15 +77,17 @@ pub(crate) struct Min {}
 | 
			
		||||
#[doc = "Std lib function: min\nThis is some function.\nIt does shit."]
 | 
			
		||||
pub(crate) const Min: Min = Min {};
 | 
			
		||||
fn boxed_min(
 | 
			
		||||
    exec_state: &mut crate::executor::ExecState,
 | 
			
		||||
    args: crate::std::Args,
 | 
			
		||||
) -> std::pin::Pin<
 | 
			
		||||
    Box<
 | 
			
		||||
        dyn std::future::Future<
 | 
			
		||||
                Output = anyhow::Result<crate::executor::KclValue, crate::errors::KclError>,
 | 
			
		||||
            > + Send,
 | 
			
		||||
            > + Send
 | 
			
		||||
            + '_,
 | 
			
		||||
    >,
 | 
			
		||||
> {
 | 
			
		||||
    Box::pin(min(args))
 | 
			
		||||
    Box::pin(min(exec_state, args))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl crate::docs::StdLibFn for Min {
 | 
			
		||||
 | 
			
		||||
@ -44,15 +44,17 @@ pub(crate) struct Show {}
 | 
			
		||||
#[doc = "Std lib function: show\nThis is some function.\nIt does shit."]
 | 
			
		||||
pub(crate) const Show: Show = Show {};
 | 
			
		||||
fn boxed_show(
 | 
			
		||||
    exec_state: &mut crate::executor::ExecState,
 | 
			
		||||
    args: crate::std::Args,
 | 
			
		||||
) -> std::pin::Pin<
 | 
			
		||||
    Box<
 | 
			
		||||
        dyn std::future::Future<
 | 
			
		||||
                Output = anyhow::Result<crate::executor::KclValue, crate::errors::KclError>,
 | 
			
		||||
            > + Send,
 | 
			
		||||
            > + Send
 | 
			
		||||
            + '_,
 | 
			
		||||
    >,
 | 
			
		||||
> {
 | 
			
		||||
    Box::pin(show(args))
 | 
			
		||||
    Box::pin(show(exec_state, args))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl crate::docs::StdLibFn for Show {
 | 
			
		||||
 | 
			
		||||
@ -44,15 +44,17 @@ pub(crate) struct Import {}
 | 
			
		||||
#[doc = "Std lib function: import\nThis is some function.\nIt does shit."]
 | 
			
		||||
pub(crate) const Import: Import = Import {};
 | 
			
		||||
fn boxed_import(
 | 
			
		||||
    exec_state: &mut crate::executor::ExecState,
 | 
			
		||||
    args: crate::std::Args,
 | 
			
		||||
) -> std::pin::Pin<
 | 
			
		||||
    Box<
 | 
			
		||||
        dyn std::future::Future<
 | 
			
		||||
                Output = anyhow::Result<crate::executor::KclValue, crate::errors::KclError>,
 | 
			
		||||
            > + Send,
 | 
			
		||||
            > + Send
 | 
			
		||||
            + '_,
 | 
			
		||||
    >,
 | 
			
		||||
> {
 | 
			
		||||
    Box::pin(import(args))
 | 
			
		||||
    Box::pin(import(exec_state, args))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl crate::docs::StdLibFn for Import {
 | 
			
		||||
 | 
			
		||||
@ -44,15 +44,17 @@ pub(crate) struct Import {}
 | 
			
		||||
#[doc = "Std lib function: import\nThis is some function.\nIt does shit."]
 | 
			
		||||
pub(crate) const Import: Import = Import {};
 | 
			
		||||
fn boxed_import(
 | 
			
		||||
    exec_state: &mut crate::executor::ExecState,
 | 
			
		||||
    args: crate::std::Args,
 | 
			
		||||
) -> std::pin::Pin<
 | 
			
		||||
    Box<
 | 
			
		||||
        dyn std::future::Future<
 | 
			
		||||
                Output = anyhow::Result<crate::executor::KclValue, crate::errors::KclError>,
 | 
			
		||||
            > + Send,
 | 
			
		||||
            > + Send
 | 
			
		||||
            + '_,
 | 
			
		||||
    >,
 | 
			
		||||
> {
 | 
			
		||||
    Box::pin(import(args))
 | 
			
		||||
    Box::pin(import(exec_state, args))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl crate::docs::StdLibFn for Import {
 | 
			
		||||
 | 
			
		||||
@ -44,15 +44,17 @@ pub(crate) struct Import {}
 | 
			
		||||
#[doc = "Std lib function: import\nThis is some function.\nIt does shit."]
 | 
			
		||||
pub(crate) const Import: Import = Import {};
 | 
			
		||||
fn boxed_import(
 | 
			
		||||
    exec_state: &mut crate::executor::ExecState,
 | 
			
		||||
    args: crate::std::Args,
 | 
			
		||||
) -> std::pin::Pin<
 | 
			
		||||
    Box<
 | 
			
		||||
        dyn std::future::Future<
 | 
			
		||||
                Output = anyhow::Result<crate::executor::KclValue, crate::errors::KclError>,
 | 
			
		||||
            > + Send,
 | 
			
		||||
            > + Send
 | 
			
		||||
            + '_,
 | 
			
		||||
    >,
 | 
			
		||||
> {
 | 
			
		||||
    Box::pin(import(args))
 | 
			
		||||
    Box::pin(import(exec_state, args))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl crate::docs::StdLibFn for Import {
 | 
			
		||||
 | 
			
		||||
@ -44,15 +44,17 @@ pub(crate) struct Show {}
 | 
			
		||||
#[doc = "Std lib function: show\nThis is some function.\nIt does shit."]
 | 
			
		||||
pub(crate) const Show: Show = Show {};
 | 
			
		||||
fn boxed_show(
 | 
			
		||||
    exec_state: &mut crate::executor::ExecState,
 | 
			
		||||
    args: crate::std::Args,
 | 
			
		||||
) -> std::pin::Pin<
 | 
			
		||||
    Box<
 | 
			
		||||
        dyn std::future::Future<
 | 
			
		||||
                Output = anyhow::Result<crate::executor::KclValue, crate::errors::KclError>,
 | 
			
		||||
            > + Send,
 | 
			
		||||
            > + Send
 | 
			
		||||
            + '_,
 | 
			
		||||
    >,
 | 
			
		||||
> {
 | 
			
		||||
    Box::pin(show(args))
 | 
			
		||||
    Box::pin(show(exec_state, args))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl crate::docs::StdLibFn for Show {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										134
									
								
								src/wasm-lib/derive-docs/tests/test_args_with_exec_state.gen
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								src/wasm-lib/derive-docs/tests/test_args_with_exec_state.gen
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,134 @@
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod test_examples_some_function {
 | 
			
		||||
    #[tokio::test(flavor = "multi_thread")]
 | 
			
		||||
    async fn test_mock_example_some_function0() {
 | 
			
		||||
        let tokens = crate::token::lexer("someFunction()").unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let ctx = crate::executor::ExecutorContext {
 | 
			
		||||
            engine: std::sync::Arc::new(Box::new(
 | 
			
		||||
                crate::engine::conn_mock::EngineConnection::new()
 | 
			
		||||
                    .await
 | 
			
		||||
                    .unwrap(),
 | 
			
		||||
            )),
 | 
			
		||||
            fs: std::sync::Arc::new(crate::fs::FileManager::new()),
 | 
			
		||||
            stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
 | 
			
		||||
            settings: Default::default(),
 | 
			
		||||
            is_mock: true,
 | 
			
		||||
        };
 | 
			
		||||
        ctx.run(&program, None).await.unwrap();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[tokio::test(flavor = "multi_thread", worker_threads = 5)]
 | 
			
		||||
    async fn kcl_test_example_some_function0() {
 | 
			
		||||
        let code = "someFunction()";
 | 
			
		||||
        let result =
 | 
			
		||||
            crate::test_server::execute_and_snapshot(code, crate::settings::types::UnitLength::Mm)
 | 
			
		||||
                .await
 | 
			
		||||
                .unwrap();
 | 
			
		||||
        twenty_twenty::assert_image(
 | 
			
		||||
            &format!("tests/outputs/{}.png", "serial_test_example_some_function0"),
 | 
			
		||||
            &result,
 | 
			
		||||
            0.99,
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[allow(non_camel_case_types, missing_docs)]
 | 
			
		||||
#[doc = "Std lib function: someFunction\nDocs"]
 | 
			
		||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, schemars :: JsonSchema, ts_rs :: TS)]
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
pub(crate) struct SomeFunction {}
 | 
			
		||||
 | 
			
		||||
#[allow(non_upper_case_globals, missing_docs)]
 | 
			
		||||
#[doc = "Std lib function: someFunction\nDocs"]
 | 
			
		||||
pub(crate) const SomeFunction: SomeFunction = SomeFunction {};
 | 
			
		||||
fn boxed_some_function(
 | 
			
		||||
    exec_state: &mut crate::executor::ExecState,
 | 
			
		||||
    args: crate::std::Args,
 | 
			
		||||
) -> std::pin::Pin<
 | 
			
		||||
    Box<
 | 
			
		||||
        dyn std::future::Future<
 | 
			
		||||
                Output = anyhow::Result<crate::executor::KclValue, crate::errors::KclError>,
 | 
			
		||||
            > + Send
 | 
			
		||||
            + '_,
 | 
			
		||||
    >,
 | 
			
		||||
> {
 | 
			
		||||
    Box::pin(some_function(exec_state, args))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl crate::docs::StdLibFn for SomeFunction {
 | 
			
		||||
    fn name(&self) -> String {
 | 
			
		||||
        "someFunction".to_string()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn summary(&self) -> String {
 | 
			
		||||
        "Docs".to_string()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn description(&self) -> String {
 | 
			
		||||
        "".to_string()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn tags(&self) -> Vec<String> {
 | 
			
		||||
        vec![]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn args(&self) -> Vec<crate::docs::StdLibFnArg> {
 | 
			
		||||
        let mut settings = schemars::gen::SchemaSettings::openapi3();
 | 
			
		||||
        settings.inline_subschemas = true;
 | 
			
		||||
        let mut generator = schemars::gen::SchemaGenerator::new(settings);
 | 
			
		||||
        vec![]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn return_value(&self) -> Option<crate::docs::StdLibFnArg> {
 | 
			
		||||
        let mut settings = schemars::gen::SchemaSettings::openapi3();
 | 
			
		||||
        settings.inline_subschemas = true;
 | 
			
		||||
        let mut generator = schemars::gen::SchemaGenerator::new(settings);
 | 
			
		||||
        Some(crate::docs::StdLibFnArg {
 | 
			
		||||
            name: "".to_string(),
 | 
			
		||||
            type_: "i32".to_string(),
 | 
			
		||||
            schema: <i32>::json_schema(&mut generator),
 | 
			
		||||
            required: true,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn unpublished(&self) -> bool {
 | 
			
		||||
        false
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn deprecated(&self) -> bool {
 | 
			
		||||
        false
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn examples(&self) -> Vec<String> {
 | 
			
		||||
        let code_blocks = vec!["someFunction()"];
 | 
			
		||||
        code_blocks
 | 
			
		||||
            .iter()
 | 
			
		||||
            .map(|cb| {
 | 
			
		||||
                let tokens = crate::token::lexer(cb).unwrap();
 | 
			
		||||
                let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
                let program = parser.ast().unwrap();
 | 
			
		||||
                let mut options: crate::ast::types::FormatOptions = Default::default();
 | 
			
		||||
                options.insert_final_newline = false;
 | 
			
		||||
                program.recast(&options, 0)
 | 
			
		||||
            })
 | 
			
		||||
            .collect::<Vec<String>>()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn std_lib_fn(&self) -> crate::std::StdFn {
 | 
			
		||||
        boxed_some_function
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn clone_box(&self) -> Box<dyn crate::docs::StdLibFn> {
 | 
			
		||||
        Box::new(self.clone())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[doc = r" Docs"]
 | 
			
		||||
#[doc = r" ```"]
 | 
			
		||||
#[doc = r" someFunction()"]
 | 
			
		||||
#[doc = r" ```"]
 | 
			
		||||
fn inner_some_function<'a>(exec_state: &mut ExecState, args: &Args) -> i32 {
 | 
			
		||||
    3
 | 
			
		||||
}
 | 
			
		||||
@ -7,6 +7,7 @@ use std::{
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use anyhow::Result;
 | 
			
		||||
use async_recursion::async_recursion;
 | 
			
		||||
use databake::*;
 | 
			
		||||
use parse_display::{Display, FromStr};
 | 
			
		||||
use schemars::JsonSchema;
 | 
			
		||||
@ -22,8 +23,8 @@ use crate::{
 | 
			
		||||
    docs::StdLibFn,
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    executor::{
 | 
			
		||||
        BodyType, DynamicState, ExecutorContext, KclValue, Metadata, PipeInfo, ProgramMemory, SketchGroup, SourceRange,
 | 
			
		||||
        StatementKind, TagEngineInfo, TagIdentifier, UserVal,
 | 
			
		||||
        BodyType, ExecState, ExecutorContext, KclValue, Metadata, SketchGroup, SourceRange, StatementKind,
 | 
			
		||||
        TagEngineInfo, TagIdentifier, UserVal,
 | 
			
		||||
    },
 | 
			
		||||
    parser::PIPE_OPERATOR,
 | 
			
		||||
    std::{kcl_stdlib::KclStdLibFn, FunctionKind},
 | 
			
		||||
@ -797,31 +798,17 @@ impl BinaryPart {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[async_recursion::async_recursion]
 | 
			
		||||
    pub async fn get_result(
 | 
			
		||||
        &self,
 | 
			
		||||
        memory: &mut ProgramMemory,
 | 
			
		||||
        dynamic_state: &DynamicState,
 | 
			
		||||
        pipe_info: &PipeInfo,
 | 
			
		||||
        ctx: &ExecutorContext,
 | 
			
		||||
    ) -> Result<KclValue, KclError> {
 | 
			
		||||
    pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
 | 
			
		||||
        match self {
 | 
			
		||||
            BinaryPart::Literal(literal) => Ok(literal.into()),
 | 
			
		||||
            BinaryPart::Identifier(identifier) => {
 | 
			
		||||
                let value = memory.get(&identifier.name, identifier.into())?;
 | 
			
		||||
                let value = exec_state.memory.get(&identifier.name, identifier.into())?;
 | 
			
		||||
                Ok(value.clone())
 | 
			
		||||
            }
 | 
			
		||||
            BinaryPart::BinaryExpression(binary_expression) => {
 | 
			
		||||
                binary_expression
 | 
			
		||||
                    .get_result(memory, dynamic_state, pipe_info, ctx)
 | 
			
		||||
                    .await
 | 
			
		||||
            }
 | 
			
		||||
            BinaryPart::CallExpression(call_expression) => {
 | 
			
		||||
                call_expression.execute(memory, dynamic_state, pipe_info, ctx).await
 | 
			
		||||
            }
 | 
			
		||||
            BinaryPart::UnaryExpression(unary_expression) => {
 | 
			
		||||
                unary_expression.get_result(memory, dynamic_state, pipe_info, ctx).await
 | 
			
		||||
            }
 | 
			
		||||
            BinaryPart::MemberExpression(member_expression) => member_expression.get_result(memory),
 | 
			
		||||
            BinaryPart::BinaryExpression(binary_expression) => binary_expression.get_result(exec_state, ctx).await,
 | 
			
		||||
            BinaryPart::CallExpression(call_expression) => call_expression.execute(exec_state, ctx).await,
 | 
			
		||||
            BinaryPart::UnaryExpression(unary_expression) => unary_expression.get_result(exec_state, ctx).await,
 | 
			
		||||
            BinaryPart::MemberExpression(member_expression) => member_expression.get_result(exec_state),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -1192,30 +1179,17 @@ impl CallExpression {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[async_recursion::async_recursion]
 | 
			
		||||
    pub async fn execute(
 | 
			
		||||
        &self,
 | 
			
		||||
        memory: &mut ProgramMemory,
 | 
			
		||||
        dynamic_state: &DynamicState,
 | 
			
		||||
        pipe_info: &PipeInfo,
 | 
			
		||||
        ctx: &ExecutorContext,
 | 
			
		||||
    ) -> Result<KclValue, KclError> {
 | 
			
		||||
        let fn_name = self.callee.name.clone();
 | 
			
		||||
    pub async fn execute(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
 | 
			
		||||
        let fn_name = &self.callee.name;
 | 
			
		||||
 | 
			
		||||
        let mut fn_args: Vec<KclValue> = Vec::with_capacity(self.arguments.len());
 | 
			
		||||
 | 
			
		||||
        for arg in &self.arguments {
 | 
			
		||||
            let metadata = Metadata {
 | 
			
		||||
                source_range: SourceRange([arg.start(), arg.end()]),
 | 
			
		||||
                source_range: SourceRange::from(arg),
 | 
			
		||||
            };
 | 
			
		||||
            let result = ctx
 | 
			
		||||
                .execute_expr(
 | 
			
		||||
                    arg,
 | 
			
		||||
                    memory,
 | 
			
		||||
                    dynamic_state,
 | 
			
		||||
                    pipe_info,
 | 
			
		||||
                    &metadata,
 | 
			
		||||
                    StatementKind::Expression,
 | 
			
		||||
                )
 | 
			
		||||
                .execute_expr(arg, exec_state, &metadata, StatementKind::Expression)
 | 
			
		||||
                .await?;
 | 
			
		||||
            fn_args.push(result);
 | 
			
		||||
        }
 | 
			
		||||
@ -1223,9 +1197,8 @@ impl CallExpression {
 | 
			
		||||
        match ctx.stdlib.get_either(&self.callee.name) {
 | 
			
		||||
            FunctionKind::Core(func) => {
 | 
			
		||||
                // Attempt to call the function.
 | 
			
		||||
                let args =
 | 
			
		||||
                    crate::std::Args::new(fn_args, self.into(), ctx.clone(), memory.clone(), dynamic_state.clone());
 | 
			
		||||
                let mut result = func.std_lib_fn()(args).await?;
 | 
			
		||||
                let args = crate::std::Args::new(fn_args, self.into(), ctx.clone());
 | 
			
		||||
                let mut result = func.std_lib_fn()(exec_state, args).await?;
 | 
			
		||||
 | 
			
		||||
                // If the return result is a sketch group or extrude group, we want to update the
 | 
			
		||||
                // memory for the tags of the group.
 | 
			
		||||
@ -1235,7 +1208,7 @@ impl CallExpression {
 | 
			
		||||
                    KclValue::UserVal(ref mut uval) => {
 | 
			
		||||
                        uval.mutate(|sketch_group: &mut SketchGroup| {
 | 
			
		||||
                            for (_, tag) in sketch_group.tags.iter() {
 | 
			
		||||
                                memory.update_tag(&tag.value, tag.clone())?;
 | 
			
		||||
                                exec_state.memory.update_tag(&tag.value, tag.clone())?;
 | 
			
		||||
                            }
 | 
			
		||||
                            Ok::<_, KclError>(())
 | 
			
		||||
                        })?;
 | 
			
		||||
@ -1275,7 +1248,7 @@ impl CallExpression {
 | 
			
		||||
                                info.sketch_group = extrude_group.id;
 | 
			
		||||
                                t.info = Some(info);
 | 
			
		||||
 | 
			
		||||
                                memory.update_tag(&tag.name, t.clone())?;
 | 
			
		||||
                                exec_state.memory.update_tag(&tag.name, t.clone())?;
 | 
			
		||||
 | 
			
		||||
                                // update the sketch group tags.
 | 
			
		||||
                                extrude_group.sketch_group.tags.insert(tag.name.clone(), t);
 | 
			
		||||
@ -1283,7 +1256,11 @@ impl CallExpression {
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        // Find the stale sketch group in memory and update it.
 | 
			
		||||
                        if let Some(current_env) = memory.environments.get_mut(memory.current_env.index()) {
 | 
			
		||||
                        if let Some(current_env) = exec_state
 | 
			
		||||
                            .memory
 | 
			
		||||
                            .environments
 | 
			
		||||
                            .get_mut(exec_state.memory.current_env.index())
 | 
			
		||||
                        {
 | 
			
		||||
                            current_env.update_sketch_group_tags(&extrude_group.sketch_group);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
@ -1294,17 +1271,18 @@ impl CallExpression {
 | 
			
		||||
            }
 | 
			
		||||
            FunctionKind::Std(func) => {
 | 
			
		||||
                let function_expression = func.function();
 | 
			
		||||
                let parts = function_expression.clone().into_parts().map_err(|e| {
 | 
			
		||||
                    KclError::Semantic(KclErrorDetails {
 | 
			
		||||
                        message: format!("Error getting parts of function: {}", e),
 | 
			
		||||
                        source_ranges: vec![self.into()],
 | 
			
		||||
                    })
 | 
			
		||||
                })?;
 | 
			
		||||
                if fn_args.len() < parts.params_required.len() || fn_args.len() > function_expression.params.len() {
 | 
			
		||||
                let (required_params, optional_params) =
 | 
			
		||||
                    function_expression.required_and_optional_params().map_err(|e| {
 | 
			
		||||
                        KclError::Semantic(KclErrorDetails {
 | 
			
		||||
                            message: format!("Error getting parts of function: {}", e),
 | 
			
		||||
                            source_ranges: vec![self.into()],
 | 
			
		||||
                        })
 | 
			
		||||
                    })?;
 | 
			
		||||
                if fn_args.len() < required_params.len() || fn_args.len() > function_expression.params.len() {
 | 
			
		||||
                    return Err(KclError::Semantic(KclErrorDetails {
 | 
			
		||||
                        message: format!(
 | 
			
		||||
                            "this function expected {} arguments, got {}",
 | 
			
		||||
                            parts.params_required.len(),
 | 
			
		||||
                            required_params.len(),
 | 
			
		||||
                            fn_args.len(),
 | 
			
		||||
                        ),
 | 
			
		||||
                        source_ranges: vec![self.into()],
 | 
			
		||||
@ -1312,8 +1290,8 @@ impl CallExpression {
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Add the arguments to the memory.
 | 
			
		||||
                let mut fn_memory = memory.clone();
 | 
			
		||||
                for (index, param) in parts.params_required.iter().enumerate() {
 | 
			
		||||
                let mut fn_memory = exec_state.memory.clone();
 | 
			
		||||
                for (index, param) in required_params.iter().enumerate() {
 | 
			
		||||
                    fn_memory.add(
 | 
			
		||||
                        ¶m.identifier.name,
 | 
			
		||||
                        fn_args.get(index).unwrap().clone(),
 | 
			
		||||
@ -1321,8 +1299,8 @@ impl CallExpression {
 | 
			
		||||
                    )?;
 | 
			
		||||
                }
 | 
			
		||||
                // Add the optional arguments to the memory.
 | 
			
		||||
                for (index, param) in parts.params_optional.iter().enumerate() {
 | 
			
		||||
                    if let Some(arg) = fn_args.get(index + parts.params_required.len()) {
 | 
			
		||||
                for (index, param) in optional_params.iter().enumerate() {
 | 
			
		||||
                    if let Some(arg) = fn_args.get(index + required_params.len()) {
 | 
			
		||||
                        fn_memory.add(¶m.identifier.name, arg.clone(), param.identifier.clone().into())?;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        fn_memory.add(
 | 
			
		||||
@ -1336,22 +1314,31 @@ impl CallExpression {
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                let mut fn_dynamic_state = dynamic_state.clone();
 | 
			
		||||
                let fn_dynamic_state = exec_state.dynamic_state.clone();
 | 
			
		||||
                // TODO: Shouldn't we merge program memory into fn_dynamic_state
 | 
			
		||||
                // here?
 | 
			
		||||
 | 
			
		||||
                // Call the stdlib function
 | 
			
		||||
                let p = func.function().clone().body;
 | 
			
		||||
                let results = match ctx
 | 
			
		||||
                    .inner_execute(&p, &mut fn_memory, &mut fn_dynamic_state, BodyType::Block)
 | 
			
		||||
                    .await
 | 
			
		||||
                {
 | 
			
		||||
                    Ok(results) => results,
 | 
			
		||||
                let p = &func.function().body;
 | 
			
		||||
 | 
			
		||||
                let (exec_result, fn_memory) = {
 | 
			
		||||
                    let previous_memory = std::mem::replace(&mut exec_state.memory, fn_memory);
 | 
			
		||||
                    let previous_dynamic_state = std::mem::replace(&mut exec_state.dynamic_state, fn_dynamic_state);
 | 
			
		||||
                    let result = ctx.inner_execute(p, exec_state, BodyType::Block).await;
 | 
			
		||||
                    exec_state.dynamic_state = previous_dynamic_state;
 | 
			
		||||
                    let fn_memory = std::mem::replace(&mut exec_state.memory, previous_memory);
 | 
			
		||||
                    (result, fn_memory)
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                match exec_result {
 | 
			
		||||
                    Ok(()) => {}
 | 
			
		||||
                    Err(err) => {
 | 
			
		||||
                        // We need to override the source ranges so we don't get the embedded kcl
 | 
			
		||||
                        // function from the stdlib.
 | 
			
		||||
                        return Err(err.override_source_ranges(vec![self.into()]));
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
                let out = results.return_;
 | 
			
		||||
                let out = fn_memory.return_;
 | 
			
		||||
                let result = out.ok_or_else(|| {
 | 
			
		||||
                    KclError::UndefinedValue(KclErrorDetails {
 | 
			
		||||
                        message: format!("Result of stdlib function {} is undefined", fn_name),
 | 
			
		||||
@ -1361,18 +1348,24 @@ impl CallExpression {
 | 
			
		||||
                Ok(result)
 | 
			
		||||
            }
 | 
			
		||||
            FunctionKind::UserDefined => {
 | 
			
		||||
                let func = memory.get(&fn_name, self.into())?;
 | 
			
		||||
                let fn_dynamic_state = dynamic_state.merge(memory);
 | 
			
		||||
                let result = func
 | 
			
		||||
                    .call_fn(fn_args, &fn_dynamic_state, ctx.clone())
 | 
			
		||||
                    .await
 | 
			
		||||
                    .map_err(|e| {
 | 
			
		||||
                        // Add the call expression to the source ranges.
 | 
			
		||||
                        e.add_source_ranges(vec![self.into()])
 | 
			
		||||
                    })?;
 | 
			
		||||
                let source_range = SourceRange::from(self);
 | 
			
		||||
                // Clone the function so that we can use a mutable reference to
 | 
			
		||||
                // exec_state.
 | 
			
		||||
                let func = exec_state.memory.get(fn_name, source_range)?.clone();
 | 
			
		||||
                let fn_dynamic_state = exec_state.dynamic_state.merge(&exec_state.memory);
 | 
			
		||||
 | 
			
		||||
                let result = result.ok_or_else(|| {
 | 
			
		||||
                    let mut source_ranges: Vec<SourceRange> = vec![self.into()];
 | 
			
		||||
                let return_value = {
 | 
			
		||||
                    let previous_dynamic_state = std::mem::replace(&mut exec_state.dynamic_state, fn_dynamic_state);
 | 
			
		||||
                    let result = func.call_fn(fn_args, exec_state, ctx.clone()).await.map_err(|e| {
 | 
			
		||||
                        // Add the call expression to the source ranges.
 | 
			
		||||
                        e.add_source_ranges(vec![source_range])
 | 
			
		||||
                    });
 | 
			
		||||
                    exec_state.dynamic_state = previous_dynamic_state;
 | 
			
		||||
                    result?
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                let result = return_value.ok_or_else(move || {
 | 
			
		||||
                    let mut source_ranges: Vec<SourceRange> = vec![source_range];
 | 
			
		||||
                    // We want to send the source range of the original function.
 | 
			
		||||
                    if let KclValue::Function { meta, .. } = func {
 | 
			
		||||
                        source_ranges = meta.iter().map(|m| m.source_range).collect();
 | 
			
		||||
@ -1990,7 +1983,7 @@ impl TagDeclarator {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn execute(&self, memory: &mut ProgramMemory) -> Result<KclValue, KclError> {
 | 
			
		||||
    pub async fn execute(&self, exec_state: &mut ExecState) -> Result<KclValue, KclError> {
 | 
			
		||||
        let memory_item = KclValue::TagIdentifier(Box::new(TagIdentifier {
 | 
			
		||||
            value: self.name.clone(),
 | 
			
		||||
            info: None,
 | 
			
		||||
@ -1999,7 +1992,7 @@ impl TagDeclarator {
 | 
			
		||||
            }],
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        memory.add(&self.name, memory_item.clone(), self.into())?;
 | 
			
		||||
        exec_state.memory.add(&self.name, memory_item.clone(), self.into())?;
 | 
			
		||||
 | 
			
		||||
        Ok(self.into())
 | 
			
		||||
    }
 | 
			
		||||
@ -2136,65 +2129,18 @@ impl ArrayExpression {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[async_recursion::async_recursion]
 | 
			
		||||
    pub async fn execute(
 | 
			
		||||
        &self,
 | 
			
		||||
        memory: &mut ProgramMemory,
 | 
			
		||||
        dynamic_state: &DynamicState,
 | 
			
		||||
        pipe_info: &PipeInfo,
 | 
			
		||||
        ctx: &ExecutorContext,
 | 
			
		||||
    ) -> Result<KclValue, KclError> {
 | 
			
		||||
    pub async fn execute(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
 | 
			
		||||
        let mut results = Vec::with_capacity(self.elements.len());
 | 
			
		||||
 | 
			
		||||
        for element in &self.elements {
 | 
			
		||||
            let result = match element {
 | 
			
		||||
                Expr::Literal(literal) => literal.into(),
 | 
			
		||||
                Expr::TagDeclarator(tag) => tag.execute(memory).await?,
 | 
			
		||||
                Expr::None(none) => none.into(),
 | 
			
		||||
                Expr::Identifier(identifier) => {
 | 
			
		||||
                    let value = memory.get(&identifier.name, identifier.into())?;
 | 
			
		||||
                    value.clone()
 | 
			
		||||
                }
 | 
			
		||||
                Expr::BinaryExpression(binary_expression) => {
 | 
			
		||||
                    binary_expression
 | 
			
		||||
                        .get_result(memory, dynamic_state, pipe_info, ctx)
 | 
			
		||||
                        .await?
 | 
			
		||||
                }
 | 
			
		||||
                Expr::CallExpression(call_expression) => {
 | 
			
		||||
                    call_expression.execute(memory, dynamic_state, pipe_info, ctx).await?
 | 
			
		||||
                }
 | 
			
		||||
                Expr::UnaryExpression(unary_expression) => {
 | 
			
		||||
                    unary_expression
 | 
			
		||||
                        .get_result(memory, dynamic_state, pipe_info, ctx)
 | 
			
		||||
                        .await?
 | 
			
		||||
                }
 | 
			
		||||
                Expr::ObjectExpression(object_expression) => {
 | 
			
		||||
                    object_expression.execute(memory, dynamic_state, pipe_info, ctx).await?
 | 
			
		||||
                }
 | 
			
		||||
                Expr::ArrayExpression(array_expression) => {
 | 
			
		||||
                    array_expression.execute(memory, dynamic_state, pipe_info, ctx).await?
 | 
			
		||||
                }
 | 
			
		||||
                Expr::PipeExpression(pipe_expression) => {
 | 
			
		||||
                    pipe_expression
 | 
			
		||||
                        .get_result(memory, dynamic_state, pipe_info, ctx)
 | 
			
		||||
                        .await?
 | 
			
		||||
                }
 | 
			
		||||
                Expr::PipeSubstitution(pipe_substitution) => {
 | 
			
		||||
                    return Err(KclError::Semantic(KclErrorDetails {
 | 
			
		||||
                        message: format!("PipeSubstitution not implemented here: {:?}", pipe_substitution),
 | 
			
		||||
                        source_ranges: vec![pipe_substitution.into()],
 | 
			
		||||
                    }));
 | 
			
		||||
                }
 | 
			
		||||
                Expr::MemberExpression(member_expression) => member_expression.get_result(memory)?,
 | 
			
		||||
                Expr::FunctionExpression(function_expression) => {
 | 
			
		||||
                    return Err(KclError::Semantic(KclErrorDetails {
 | 
			
		||||
                        message: format!("FunctionExpression not implemented here: {:?}", function_expression),
 | 
			
		||||
                        source_ranges: vec![function_expression.into()],
 | 
			
		||||
                    }));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            .get_json_value()?;
 | 
			
		||||
            let metadata = Metadata::from(element);
 | 
			
		||||
            // TODO: Carry statement kind here so that we know if we're
 | 
			
		||||
            // inside a variable declaration.
 | 
			
		||||
            let value = ctx
 | 
			
		||||
                .execute_expr(element, exec_state, &metadata, StatementKind::Expression)
 | 
			
		||||
                .await?;
 | 
			
		||||
 | 
			
		||||
            results.push(result);
 | 
			
		||||
            results.push(value.get_json_value()?);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Ok(KclValue::UserVal(UserVal {
 | 
			
		||||
@ -2279,61 +2225,13 @@ impl ObjectExpression {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[async_recursion::async_recursion]
 | 
			
		||||
    pub async fn execute(
 | 
			
		||||
        &self,
 | 
			
		||||
        memory: &mut ProgramMemory,
 | 
			
		||||
        dynamic_state: &DynamicState,
 | 
			
		||||
        pipe_info: &PipeInfo,
 | 
			
		||||
        ctx: &ExecutorContext,
 | 
			
		||||
    ) -> Result<KclValue, KclError> {
 | 
			
		||||
    pub async fn execute(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
 | 
			
		||||
        let mut object = Map::new();
 | 
			
		||||
        for property in &self.properties {
 | 
			
		||||
            let result = match &property.value {
 | 
			
		||||
                Expr::Literal(literal) => literal.into(),
 | 
			
		||||
                Expr::TagDeclarator(tag) => tag.execute(memory).await?,
 | 
			
		||||
                Expr::None(none) => none.into(),
 | 
			
		||||
                Expr::Identifier(identifier) => {
 | 
			
		||||
                    let value = memory.get(&identifier.name, identifier.into())?;
 | 
			
		||||
                    value.clone()
 | 
			
		||||
                }
 | 
			
		||||
                Expr::BinaryExpression(binary_expression) => {
 | 
			
		||||
                    binary_expression
 | 
			
		||||
                        .get_result(memory, dynamic_state, pipe_info, ctx)
 | 
			
		||||
                        .await?
 | 
			
		||||
                }
 | 
			
		||||
                Expr::CallExpression(call_expression) => {
 | 
			
		||||
                    call_expression.execute(memory, dynamic_state, pipe_info, ctx).await?
 | 
			
		||||
                }
 | 
			
		||||
                Expr::UnaryExpression(unary_expression) => {
 | 
			
		||||
                    unary_expression
 | 
			
		||||
                        .get_result(memory, dynamic_state, pipe_info, ctx)
 | 
			
		||||
                        .await?
 | 
			
		||||
                }
 | 
			
		||||
                Expr::ObjectExpression(object_expression) => {
 | 
			
		||||
                    object_expression.execute(memory, dynamic_state, pipe_info, ctx).await?
 | 
			
		||||
                }
 | 
			
		||||
                Expr::ArrayExpression(array_expression) => {
 | 
			
		||||
                    array_expression.execute(memory, dynamic_state, pipe_info, ctx).await?
 | 
			
		||||
                }
 | 
			
		||||
                Expr::PipeExpression(pipe_expression) => {
 | 
			
		||||
                    pipe_expression
 | 
			
		||||
                        .get_result(memory, dynamic_state, pipe_info, ctx)
 | 
			
		||||
                        .await?
 | 
			
		||||
                }
 | 
			
		||||
                Expr::MemberExpression(member_expression) => member_expression.get_result(memory)?,
 | 
			
		||||
                Expr::PipeSubstitution(pipe_substitution) => {
 | 
			
		||||
                    return Err(KclError::Semantic(KclErrorDetails {
 | 
			
		||||
                        message: format!("PipeSubstitution not implemented here: {:?}", pipe_substitution),
 | 
			
		||||
                        source_ranges: vec![pipe_substitution.into()],
 | 
			
		||||
                    }));
 | 
			
		||||
                }
 | 
			
		||||
                Expr::FunctionExpression(function_expression) => {
 | 
			
		||||
                    return Err(KclError::Semantic(KclErrorDetails {
 | 
			
		||||
                        message: format!("FunctionExpression not implemented here: {:?}", function_expression),
 | 
			
		||||
                        source_ranges: vec![function_expression.into()],
 | 
			
		||||
                    }));
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
            let metadata = Metadata::from(&property.value);
 | 
			
		||||
            let result = ctx
 | 
			
		||||
                .execute_expr(&property.value, exec_state, &metadata, StatementKind::Expression)
 | 
			
		||||
                .await?;
 | 
			
		||||
 | 
			
		||||
            object.insert(property.key.name.clone(), result.get_json_value()?);
 | 
			
		||||
        }
 | 
			
		||||
@ -2545,11 +2443,11 @@ impl MemberExpression {
 | 
			
		||||
        None
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn get_result_array(&self, memory: &mut ProgramMemory, index: usize) -> Result<KclValue, KclError> {
 | 
			
		||||
    pub fn get_result_array(&self, exec_state: &mut ExecState, index: usize) -> Result<KclValue, KclError> {
 | 
			
		||||
        let array = match &self.object {
 | 
			
		||||
            MemberObject::MemberExpression(member_expr) => member_expr.get_result(memory)?,
 | 
			
		||||
            MemberObject::MemberExpression(member_expr) => member_expr.get_result(exec_state)?,
 | 
			
		||||
            MemberObject::Identifier(identifier) => {
 | 
			
		||||
                let value = memory.get(&identifier.name, identifier.into())?;
 | 
			
		||||
                let value = exec_state.memory.get(&identifier.name, identifier.into())?;
 | 
			
		||||
                value.clone()
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
@ -2578,7 +2476,7 @@ impl MemberExpression {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn get_result(&self, memory: &mut ProgramMemory) -> Result<KclValue, KclError> {
 | 
			
		||||
    pub fn get_result(&self, exec_state: &mut ExecState) -> Result<KclValue, KclError> {
 | 
			
		||||
        #[derive(Debug)]
 | 
			
		||||
        enum Property {
 | 
			
		||||
            Number(usize),
 | 
			
		||||
@ -2605,7 +2503,7 @@ impl MemberExpression {
 | 
			
		||||
                    Property::String(name.to_string())
 | 
			
		||||
                } else {
 | 
			
		||||
                    // Actually evaluate memory to compute the property.
 | 
			
		||||
                    let prop = memory.get(&name, property_src)?;
 | 
			
		||||
                    let prop = exec_state.memory.get(&name, property_src)?;
 | 
			
		||||
                    let KclValue::UserVal(prop) = prop else {
 | 
			
		||||
                        return Err(KclError::Semantic(KclErrorDetails {
 | 
			
		||||
                            source_ranges: property_sr,
 | 
			
		||||
@ -2667,9 +2565,9 @@ impl MemberExpression {
 | 
			
		||||
 | 
			
		||||
        let object = match &self.object {
 | 
			
		||||
            // TODO: Don't use recursion here, use a loop.
 | 
			
		||||
            MemberObject::MemberExpression(member_expr) => member_expr.get_result(memory)?,
 | 
			
		||||
            MemberObject::MemberExpression(member_expr) => member_expr.get_result(exec_state)?,
 | 
			
		||||
            MemberObject::Identifier(identifier) => {
 | 
			
		||||
                let value = memory.get(&identifier.name, identifier.into())?;
 | 
			
		||||
                let value = exec_state.memory.get(&identifier.name, identifier.into())?;
 | 
			
		||||
                value.clone()
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
@ -2827,23 +2725,9 @@ impl BinaryExpression {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[async_recursion::async_recursion]
 | 
			
		||||
    pub async fn get_result(
 | 
			
		||||
        &self,
 | 
			
		||||
        memory: &mut ProgramMemory,
 | 
			
		||||
        dynamic_state: &DynamicState,
 | 
			
		||||
        pipe_info: &PipeInfo,
 | 
			
		||||
        ctx: &ExecutorContext,
 | 
			
		||||
    ) -> Result<KclValue, KclError> {
 | 
			
		||||
        let left_json_value = self
 | 
			
		||||
            .left
 | 
			
		||||
            .get_result(memory, dynamic_state, pipe_info, ctx)
 | 
			
		||||
            .await?
 | 
			
		||||
            .get_json_value()?;
 | 
			
		||||
        let right_json_value = self
 | 
			
		||||
            .right
 | 
			
		||||
            .get_result(memory, dynamic_state, pipe_info, ctx)
 | 
			
		||||
            .await?
 | 
			
		||||
            .get_json_value()?;
 | 
			
		||||
    pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
 | 
			
		||||
        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()?;
 | 
			
		||||
 | 
			
		||||
        // First check if we are doing string concatenation.
 | 
			
		||||
        if self.operator == BinaryOperator::Add {
 | 
			
		||||
@ -3043,19 +2927,9 @@ impl UnaryExpression {
 | 
			
		||||
        self.argument.get_constraint_level()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn get_result(
 | 
			
		||||
        &self,
 | 
			
		||||
        memory: &mut ProgramMemory,
 | 
			
		||||
        dynamic_state: &DynamicState,
 | 
			
		||||
        pipe_info: &PipeInfo,
 | 
			
		||||
        ctx: &ExecutorContext,
 | 
			
		||||
    ) -> Result<KclValue, KclError> {
 | 
			
		||||
    pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
 | 
			
		||||
        if self.operator == UnaryOperator::Not {
 | 
			
		||||
            let value = self
 | 
			
		||||
                .argument
 | 
			
		||||
                .get_result(memory, dynamic_state, pipe_info, ctx)
 | 
			
		||||
                .await?
 | 
			
		||||
                .get_json_value()?;
 | 
			
		||||
            let value = self.argument.get_result(exec_state, ctx).await?.get_json_value()?;
 | 
			
		||||
            let Some(bool_value) = json_as_bool(&value) else {
 | 
			
		||||
                return Err(KclError::Semantic(KclErrorDetails {
 | 
			
		||||
                    message: format!("Cannot apply unary operator ! to non-boolean value: {}", value),
 | 
			
		||||
@ -3072,11 +2946,7 @@ impl UnaryExpression {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let num = parse_json_number_as_f64(
 | 
			
		||||
            &self
 | 
			
		||||
                .argument
 | 
			
		||||
                .get_result(memory, dynamic_state, pipe_info, ctx)
 | 
			
		||||
                .await?
 | 
			
		||||
                .get_json_value()?,
 | 
			
		||||
            &self.argument.get_result(exec_state, ctx).await?.get_json_value()?,
 | 
			
		||||
            self.into(),
 | 
			
		||||
        )?;
 | 
			
		||||
        Ok(KclValue::UserVal(UserVal {
 | 
			
		||||
@ -3204,14 +3074,9 @@ impl PipeExpression {
 | 
			
		||||
        None
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn get_result(
 | 
			
		||||
        &self,
 | 
			
		||||
        memory: &mut ProgramMemory,
 | 
			
		||||
        dynamic_state: &DynamicState,
 | 
			
		||||
        pipe_info: &PipeInfo,
 | 
			
		||||
        ctx: &ExecutorContext,
 | 
			
		||||
    ) -> Result<KclValue, KclError> {
 | 
			
		||||
        execute_pipe_body(memory, dynamic_state, &self.body, pipe_info, self.into(), ctx).await
 | 
			
		||||
    #[async_recursion]
 | 
			
		||||
    pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
 | 
			
		||||
        execute_pipe_body(exec_state, &self.body, self.into(), ctx).await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Rename all identifiers that have the old name to the new given name.
 | 
			
		||||
@ -3222,45 +3087,49 @@ impl PipeExpression {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[async_recursion::async_recursion]
 | 
			
		||||
async fn execute_pipe_body(
 | 
			
		||||
    memory: &mut ProgramMemory,
 | 
			
		||||
    dynamic_state: &DynamicState,
 | 
			
		||||
    exec_state: &mut ExecState,
 | 
			
		||||
    body: &[Expr],
 | 
			
		||||
    pipe_info: &PipeInfo,
 | 
			
		||||
    source_range: SourceRange,
 | 
			
		||||
    ctx: &ExecutorContext,
 | 
			
		||||
) -> Result<KclValue, KclError> {
 | 
			
		||||
    let mut body = body.iter();
 | 
			
		||||
    let first = body.next().ok_or_else(|| {
 | 
			
		||||
        KclError::Semantic(KclErrorDetails {
 | 
			
		||||
    let Some((first, body)) = body.split_first() else {
 | 
			
		||||
        return Err(KclError::Semantic(KclErrorDetails {
 | 
			
		||||
            message: "Pipe expressions cannot be empty".to_owned(),
 | 
			
		||||
            source_ranges: vec![source_range],
 | 
			
		||||
        })
 | 
			
		||||
    })?;
 | 
			
		||||
        }));
 | 
			
		||||
    };
 | 
			
		||||
    // Evaluate the first element in the pipeline.
 | 
			
		||||
    // They use the `pipe_info` from some AST node above this, so that if pipe expression is nested in a larger pipe expression,
 | 
			
		||||
    // They use the pipe_value from some AST node above this, so that if pipe expression is nested in a larger pipe expression,
 | 
			
		||||
    // they use the % from the parent. After all, this pipe expression hasn't been executed yet, so it doesn't have any % value
 | 
			
		||||
    // of its own.
 | 
			
		||||
    let meta = Metadata {
 | 
			
		||||
        source_range: SourceRange([first.start(), first.end()]),
 | 
			
		||||
    };
 | 
			
		||||
    let output = ctx
 | 
			
		||||
        .execute_expr(
 | 
			
		||||
            first,
 | 
			
		||||
            memory,
 | 
			
		||||
            dynamic_state,
 | 
			
		||||
            pipe_info,
 | 
			
		||||
            &meta,
 | 
			
		||||
            StatementKind::Expression,
 | 
			
		||||
        )
 | 
			
		||||
        .execute_expr(first, exec_state, &meta, StatementKind::Expression)
 | 
			
		||||
        .await?;
 | 
			
		||||
 | 
			
		||||
    // Now that we've evaluated the first child expression in the pipeline, following child expressions
 | 
			
		||||
    // should use the previous child expression for %.
 | 
			
		||||
    // This means there's no more need for the previous `pipe_info` from the parent AST node above this one.
 | 
			
		||||
    let mut new_pipe_info = PipeInfo::new();
 | 
			
		||||
    new_pipe_info.previous_results = Some(output);
 | 
			
		||||
    // This means there's no more need for the previous pipe_value from the parent AST node above this one.
 | 
			
		||||
    let previous_pipe_value = std::mem::replace(&mut exec_state.pipe_value, Some(output));
 | 
			
		||||
    // Evaluate remaining elements.
 | 
			
		||||
    let result = inner_execute_pipe_body(exec_state, body, ctx).await;
 | 
			
		||||
    // Restore the previous pipe value.
 | 
			
		||||
    exec_state.pipe_value = previous_pipe_value;
 | 
			
		||||
 | 
			
		||||
    result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Execute the tail of a pipe expression.  exec_state.pipe_value must be set by
 | 
			
		||||
/// the caller.
 | 
			
		||||
#[async_recursion]
 | 
			
		||||
async fn inner_execute_pipe_body(
 | 
			
		||||
    exec_state: &mut ExecState,
 | 
			
		||||
    body: &[Expr],
 | 
			
		||||
    ctx: &ExecutorContext,
 | 
			
		||||
) -> Result<KclValue, KclError> {
 | 
			
		||||
    for expression in body {
 | 
			
		||||
        match expression {
 | 
			
		||||
            Expr::TagDeclarator(_) => {
 | 
			
		||||
@ -3286,19 +3155,12 @@ async fn execute_pipe_body(
 | 
			
		||||
            source_range: SourceRange([expression.start(), expression.end()]),
 | 
			
		||||
        };
 | 
			
		||||
        let output = ctx
 | 
			
		||||
            .execute_expr(
 | 
			
		||||
                expression,
 | 
			
		||||
                memory,
 | 
			
		||||
                dynamic_state,
 | 
			
		||||
                &new_pipe_info,
 | 
			
		||||
                &metadata,
 | 
			
		||||
                StatementKind::Expression,
 | 
			
		||||
            )
 | 
			
		||||
            .execute_expr(expression, exec_state, &metadata, StatementKind::Expression)
 | 
			
		||||
            .await?;
 | 
			
		||||
        new_pipe_info.previous_results = Some(output);
 | 
			
		||||
        exec_state.pipe_value = Some(output);
 | 
			
		||||
    }
 | 
			
		||||
    // Safe to unwrap here, because `newpipe_info` always has something pushed in when the `match first` executes.
 | 
			
		||||
    let final_output = new_pipe_info.previous_results.unwrap();
 | 
			
		||||
    // Safe to unwrap here, because pipe_value always has something pushed in when the `match first` executes.
 | 
			
		||||
    let final_output = exec_state.pipe_value.take().unwrap();
 | 
			
		||||
    Ok(final_output)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -3430,14 +3292,6 @@ pub struct FunctionExpression {
 | 
			
		||||
 | 
			
		||||
impl_value_meta!(FunctionExpression);
 | 
			
		||||
 | 
			
		||||
pub struct FunctionExpressionParts {
 | 
			
		||||
    pub start: usize,
 | 
			
		||||
    pub end: usize,
 | 
			
		||||
    pub params_required: Vec<Parameter>,
 | 
			
		||||
    pub params_optional: Vec<Parameter>,
 | 
			
		||||
    pub body: Program,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, PartialEq, Clone)]
 | 
			
		||||
pub struct RequiredParamAfterOptionalParam(pub Parameter);
 | 
			
		||||
 | 
			
		||||
@ -3472,36 +3326,28 @@ impl FunctionExpression {
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    pub fn into_parts(self) -> Result<FunctionExpressionParts, RequiredParamAfterOptionalParam> {
 | 
			
		||||
    pub fn required_and_optional_params(
 | 
			
		||||
        &self,
 | 
			
		||||
    ) -> Result<(&[Parameter], &[Parameter]), RequiredParamAfterOptionalParam> {
 | 
			
		||||
        let Self {
 | 
			
		||||
            start,
 | 
			
		||||
            end,
 | 
			
		||||
            start: _,
 | 
			
		||||
            end: _,
 | 
			
		||||
            params,
 | 
			
		||||
            body,
 | 
			
		||||
            body: _,
 | 
			
		||||
            digest: _,
 | 
			
		||||
            return_type: _,
 | 
			
		||||
        } = self;
 | 
			
		||||
        let mut params_required = Vec::with_capacity(params.len());
 | 
			
		||||
        let mut params_optional = Vec::with_capacity(params.len());
 | 
			
		||||
        let mut found_optional = false;
 | 
			
		||||
        for param in params {
 | 
			
		||||
            if param.optional {
 | 
			
		||||
                params_optional.push(param);
 | 
			
		||||
            } else {
 | 
			
		||||
                if !params_optional.is_empty() {
 | 
			
		||||
                    return Err(RequiredParamAfterOptionalParam(param));
 | 
			
		||||
                }
 | 
			
		||||
                params_required.push(param);
 | 
			
		||||
                found_optional = true;
 | 
			
		||||
            } else if found_optional {
 | 
			
		||||
                return Err(RequiredParamAfterOptionalParam(param.clone()));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        params_required.shrink_to_fit();
 | 
			
		||||
        params_optional.shrink_to_fit();
 | 
			
		||||
        Ok(FunctionExpressionParts {
 | 
			
		||||
            start,
 | 
			
		||||
            end,
 | 
			
		||||
            params_required,
 | 
			
		||||
            params_optional,
 | 
			
		||||
            body,
 | 
			
		||||
        })
 | 
			
		||||
        let boundary = self.params.partition_point(|param| !param.optional);
 | 
			
		||||
        // SAFETY: split_at panics if the boundary is greater than the length.
 | 
			
		||||
        Ok(self.params.split_at(boundary))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Required parameters must be declared before optional parameters.
 | 
			
		||||
 | 
			
		||||
@ -23,6 +23,20 @@ use crate::{
 | 
			
		||||
    std::{FnAsArg, StdLib},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// State for executing a program.
 | 
			
		||||
#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
#[serde(rename_all = "camelCase")]
 | 
			
		||||
pub struct ExecState {
 | 
			
		||||
    /// Program variable bindings.
 | 
			
		||||
    pub memory: ProgramMemory,
 | 
			
		||||
    /// Dynamic state that follows dynamic flow of the program.
 | 
			
		||||
    pub dynamic_state: DynamicState,
 | 
			
		||||
    /// The current value of the pipe operator returned from the previous
 | 
			
		||||
    /// expression.  If we're not currently in a pipeline, this will be None.
 | 
			
		||||
    pub pipe_value: Option<KclValue>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
#[serde(rename_all = "camelCase")]
 | 
			
		||||
@ -227,7 +241,7 @@ impl Environment {
 | 
			
		||||
/// Dynamic state that depends on the dynamic flow of the program, like the call
 | 
			
		||||
/// stack.  If the language had exceptions, for example, you could store the
 | 
			
		||||
/// stack of exception handlers here.
 | 
			
		||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, ts_rs::TS, JsonSchema)]
 | 
			
		||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize, ts_rs::TS, JsonSchema)]
 | 
			
		||||
pub struct DynamicState {
 | 
			
		||||
    pub extrude_group_ids: Vec<ExtrudeGroupLazyIds>,
 | 
			
		||||
}
 | 
			
		||||
@ -730,25 +744,10 @@ pub type MemoryFunction =
 | 
			
		||||
        memory: ProgramMemory,
 | 
			
		||||
        expression: Box<FunctionExpression>,
 | 
			
		||||
        metadata: Vec<Metadata>,
 | 
			
		||||
        dynamic_state: DynamicState,
 | 
			
		||||
        exec_state: &ExecState,
 | 
			
		||||
        ctx: ExecutorContext,
 | 
			
		||||
    ) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<Option<KclValue>, KclError>> + Send>>;
 | 
			
		||||
 | 
			
		||||
fn force_memory_function<
 | 
			
		||||
    F: Fn(
 | 
			
		||||
        Vec<KclValue>,
 | 
			
		||||
        ProgramMemory,
 | 
			
		||||
        Box<FunctionExpression>,
 | 
			
		||||
        Vec<Metadata>,
 | 
			
		||||
        DynamicState,
 | 
			
		||||
        ExecutorContext,
 | 
			
		||||
    ) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<Option<KclValue>, KclError>> + Send>>,
 | 
			
		||||
>(
 | 
			
		||||
    f: F,
 | 
			
		||||
) -> F {
 | 
			
		||||
    f
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<KclValue> for Vec<SourceRange> {
 | 
			
		||||
    fn from(item: KclValue) -> Self {
 | 
			
		||||
        match item {
 | 
			
		||||
@ -848,9 +847,8 @@ impl KclValue {
 | 
			
		||||
        else {
 | 
			
		||||
            return None;
 | 
			
		||||
        };
 | 
			
		||||
        let func = func.as_ref()?;
 | 
			
		||||
        Some(FnAsArg {
 | 
			
		||||
            func,
 | 
			
		||||
            func: func.as_ref(),
 | 
			
		||||
            expr: expression.to_owned(),
 | 
			
		||||
            memory: memory.to_owned(),
 | 
			
		||||
        })
 | 
			
		||||
@ -904,7 +902,7 @@ impl KclValue {
 | 
			
		||||
    pub async fn call_fn(
 | 
			
		||||
        &self,
 | 
			
		||||
        args: Vec<KclValue>,
 | 
			
		||||
        dynamic_state: &DynamicState,
 | 
			
		||||
        exec_state: &mut ExecState,
 | 
			
		||||
        ctx: ExecutorContext,
 | 
			
		||||
    ) -> Result<Option<KclValue>, KclError> {
 | 
			
		||||
        let KclValue::Function {
 | 
			
		||||
@ -919,21 +917,19 @@ impl KclValue {
 | 
			
		||||
                source_ranges: vec![],
 | 
			
		||||
            }));
 | 
			
		||||
        };
 | 
			
		||||
        let Some(func) = func else {
 | 
			
		||||
            return Err(KclError::Semantic(KclErrorDetails {
 | 
			
		||||
                message: format!("Not a function: {:?}", expression),
 | 
			
		||||
                source_ranges: vec![],
 | 
			
		||||
            }));
 | 
			
		||||
        };
 | 
			
		||||
        func(
 | 
			
		||||
            args,
 | 
			
		||||
            closure_memory.as_ref().clone(),
 | 
			
		||||
            expression.clone(),
 | 
			
		||||
            meta.clone(),
 | 
			
		||||
            dynamic_state.clone(),
 | 
			
		||||
            ctx,
 | 
			
		||||
        )
 | 
			
		||||
        .await
 | 
			
		||||
        if let Some(func) = func {
 | 
			
		||||
            func(
 | 
			
		||||
                args,
 | 
			
		||||
                closure_memory.as_ref().clone(),
 | 
			
		||||
                expression.clone(),
 | 
			
		||||
                meta.clone(),
 | 
			
		||||
                exec_state,
 | 
			
		||||
                ctx,
 | 
			
		||||
            )
 | 
			
		||||
            .await
 | 
			
		||||
        } else {
 | 
			
		||||
            call_user_defined_function(args, closure_memory.as_ref(), expression.as_ref(), exec_state, &ctx).await
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1356,6 +1352,14 @@ impl From<&ReturnStatement> for Metadata {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<&Expr> for Metadata {
 | 
			
		||||
    fn from(expr: &Expr) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            source_range: SourceRange::from(expr),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// A base path.
 | 
			
		||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
@ -1573,25 +1577,6 @@ impl ExtrudeSurface {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
#[serde(rename_all = "camelCase")]
 | 
			
		||||
pub struct PipeInfo {
 | 
			
		||||
    pub previous_results: Option<KclValue>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl PipeInfo {
 | 
			
		||||
    pub fn new() -> Self {
 | 
			
		||||
        Self { previous_results: None }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for PipeInfo {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Self::new()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// The executor context.
 | 
			
		||||
/// Cloning will return another handle to the same engine connection/session,
 | 
			
		||||
/// as this uses `Arc` under the hood.
 | 
			
		||||
@ -1773,7 +1758,7 @@ impl ExecutorContext {
 | 
			
		||||
        &self,
 | 
			
		||||
        program: &crate::ast::types::Program,
 | 
			
		||||
        memory: Option<ProgramMemory>,
 | 
			
		||||
    ) -> Result<ProgramMemory, KclError> {
 | 
			
		||||
    ) -> Result<ExecState, KclError> {
 | 
			
		||||
        self.run_with_session_data(program, memory).await.map(|x| x.0)
 | 
			
		||||
    }
 | 
			
		||||
    /// Perform the execution of a program.
 | 
			
		||||
@ -1783,7 +1768,7 @@ impl ExecutorContext {
 | 
			
		||||
        &self,
 | 
			
		||||
        program: &crate::ast::types::Program,
 | 
			
		||||
        memory: Option<ProgramMemory>,
 | 
			
		||||
    ) -> Result<(ProgramMemory, Option<ModelingSessionData>), KclError> {
 | 
			
		||||
    ) -> Result<(ExecState, Option<ModelingSessionData>), KclError> {
 | 
			
		||||
        // Before we even start executing the program, set the units.
 | 
			
		||||
        self.engine
 | 
			
		||||
            .batch_modeling_cmd(
 | 
			
		||||
@ -1794,22 +1779,19 @@ impl ExecutorContext {
 | 
			
		||||
                },
 | 
			
		||||
            )
 | 
			
		||||
            .await?;
 | 
			
		||||
        let mut memory = if let Some(memory) = memory {
 | 
			
		||||
        let memory = if let Some(memory) = memory {
 | 
			
		||||
            memory.clone()
 | 
			
		||||
        } else {
 | 
			
		||||
            Default::default()
 | 
			
		||||
        };
 | 
			
		||||
        let mut dynamic_state = DynamicState::default();
 | 
			
		||||
        let final_memory = self
 | 
			
		||||
            .inner_execute(
 | 
			
		||||
                program,
 | 
			
		||||
                &mut memory,
 | 
			
		||||
                &mut dynamic_state,
 | 
			
		||||
                crate::executor::BodyType::Root,
 | 
			
		||||
            )
 | 
			
		||||
        let mut exec_state = ExecState {
 | 
			
		||||
            memory,
 | 
			
		||||
            ..Default::default()
 | 
			
		||||
        };
 | 
			
		||||
        self.inner_execute(program, &mut exec_state, crate::executor::BodyType::Root)
 | 
			
		||||
            .await?;
 | 
			
		||||
        let session_data = self.engine.get_session_data();
 | 
			
		||||
        Ok((final_memory, session_data))
 | 
			
		||||
        Ok((exec_state, session_data))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Execute an AST's program.
 | 
			
		||||
@ -1817,12 +1799,9 @@ impl ExecutorContext {
 | 
			
		||||
    pub(crate) async fn inner_execute(
 | 
			
		||||
        &self,
 | 
			
		||||
        program: &crate::ast::types::Program,
 | 
			
		||||
        memory: &mut ProgramMemory,
 | 
			
		||||
        dynamic_state: &mut DynamicState,
 | 
			
		||||
        exec_state: &mut ExecState,
 | 
			
		||||
        body_type: BodyType,
 | 
			
		||||
    ) -> Result<ProgramMemory, KclError> {
 | 
			
		||||
        let pipe_info = PipeInfo::default();
 | 
			
		||||
 | 
			
		||||
    ) -> Result<(), KclError> {
 | 
			
		||||
        // Iterate over the body of the program.
 | 
			
		||||
        for statement in &program.body {
 | 
			
		||||
            match statement {
 | 
			
		||||
@ -1831,9 +1810,7 @@ impl ExecutorContext {
 | 
			
		||||
                    // Discard return value.
 | 
			
		||||
                    self.execute_expr(
 | 
			
		||||
                        &expression_statement.expression,
 | 
			
		||||
                        memory,
 | 
			
		||||
                        dynamic_state,
 | 
			
		||||
                        &pipe_info,
 | 
			
		||||
                        exec_state,
 | 
			
		||||
                        &metadata,
 | 
			
		||||
                        StatementKind::Expression,
 | 
			
		||||
                    )
 | 
			
		||||
@ -1848,14 +1825,12 @@ impl ExecutorContext {
 | 
			
		||||
                        let memory_item = self
 | 
			
		||||
                            .execute_expr(
 | 
			
		||||
                                &declaration.init,
 | 
			
		||||
                                memory,
 | 
			
		||||
                                dynamic_state,
 | 
			
		||||
                                &pipe_info,
 | 
			
		||||
                                exec_state,
 | 
			
		||||
                                &metadata,
 | 
			
		||||
                                StatementKind::Declaration { name: &var_name },
 | 
			
		||||
                            )
 | 
			
		||||
                            .await?;
 | 
			
		||||
                        memory.add(&var_name, memory_item, source_range)?;
 | 
			
		||||
                        exec_state.memory.add(&var_name, memory_item, source_range)?;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                BodyItem::ReturnStatement(return_statement) => {
 | 
			
		||||
@ -1863,14 +1838,12 @@ impl ExecutorContext {
 | 
			
		||||
                    let value = self
 | 
			
		||||
                        .execute_expr(
 | 
			
		||||
                            &return_statement.argument,
 | 
			
		||||
                            memory,
 | 
			
		||||
                            dynamic_state,
 | 
			
		||||
                            &pipe_info,
 | 
			
		||||
                            exec_state,
 | 
			
		||||
                            &metadata,
 | 
			
		||||
                            StatementKind::Expression,
 | 
			
		||||
                        )
 | 
			
		||||
                        .await?;
 | 
			
		||||
                    memory.return_ = Some(value);
 | 
			
		||||
                    exec_state.memory.return_ = Some(value);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@ -1887,81 +1860,38 @@ impl ExecutorContext {
 | 
			
		||||
                .await?;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Ok(memory.clone())
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn execute_expr<'a>(
 | 
			
		||||
        &self,
 | 
			
		||||
        init: &Expr,
 | 
			
		||||
        memory: &mut ProgramMemory,
 | 
			
		||||
        dynamic_state: &DynamicState,
 | 
			
		||||
        pipe_info: &PipeInfo,
 | 
			
		||||
        exec_state: &mut ExecState,
 | 
			
		||||
        metadata: &Metadata,
 | 
			
		||||
        statement_kind: StatementKind<'a>,
 | 
			
		||||
    ) -> Result<KclValue, KclError> {
 | 
			
		||||
        let item = match init {
 | 
			
		||||
            Expr::None(none) => KclValue::from(none),
 | 
			
		||||
            Expr::Literal(literal) => KclValue::from(literal),
 | 
			
		||||
            Expr::TagDeclarator(tag) => tag.execute(memory).await?,
 | 
			
		||||
            Expr::TagDeclarator(tag) => tag.execute(exec_state).await?,
 | 
			
		||||
            Expr::Identifier(identifier) => {
 | 
			
		||||
                let value = memory.get(&identifier.name, identifier.into())?;
 | 
			
		||||
                let value = exec_state.memory.get(&identifier.name, identifier.into())?;
 | 
			
		||||
                value.clone()
 | 
			
		||||
            }
 | 
			
		||||
            Expr::BinaryExpression(binary_expression) => {
 | 
			
		||||
                binary_expression
 | 
			
		||||
                    .get_result(memory, dynamic_state, pipe_info, self)
 | 
			
		||||
                    .await?
 | 
			
		||||
            }
 | 
			
		||||
            Expr::BinaryExpression(binary_expression) => binary_expression.get_result(exec_state, self).await?,
 | 
			
		||||
            Expr::FunctionExpression(function_expression) => {
 | 
			
		||||
                let mem_func = force_memory_function(
 | 
			
		||||
                    |args: Vec<KclValue>,
 | 
			
		||||
                     memory: ProgramMemory,
 | 
			
		||||
                     function_expression: Box<FunctionExpression>,
 | 
			
		||||
                     _metadata: Vec<Metadata>,
 | 
			
		||||
                     mut dynamic_state: DynamicState,
 | 
			
		||||
                     ctx: ExecutorContext| {
 | 
			
		||||
                        Box::pin(async move {
 | 
			
		||||
                            // Create a new environment to execute the function
 | 
			
		||||
                            // body in so that local variables shadow variables
 | 
			
		||||
                            // in the parent scope.  The new environment's
 | 
			
		||||
                            // parent should be the environment of the closure.
 | 
			
		||||
                            let mut body_memory = memory.clone();
 | 
			
		||||
                            let closure_env = memory.current_env;
 | 
			
		||||
                            let body_env = body_memory.new_env_for_call(closure_env);
 | 
			
		||||
                            body_memory.current_env = body_env;
 | 
			
		||||
                            let mut fn_memory = assign_args_to_params(&function_expression, args, body_memory)?;
 | 
			
		||||
 | 
			
		||||
                            let result = ctx
 | 
			
		||||
                                .inner_execute(
 | 
			
		||||
                                    &function_expression.body,
 | 
			
		||||
                                    &mut fn_memory,
 | 
			
		||||
                                    &mut dynamic_state,
 | 
			
		||||
                                    BodyType::Block,
 | 
			
		||||
                                )
 | 
			
		||||
                                .await?;
 | 
			
		||||
 | 
			
		||||
                            Ok(result.return_)
 | 
			
		||||
                        })
 | 
			
		||||
                    },
 | 
			
		||||
                );
 | 
			
		||||
                // Cloning memory here is crucial for semantics so that we close
 | 
			
		||||
                // over variables.  Variables defined lexically later shouldn't
 | 
			
		||||
                // be available to the function body.
 | 
			
		||||
                KclValue::Function {
 | 
			
		||||
                    expression: function_expression.clone(),
 | 
			
		||||
                    meta: vec![metadata.to_owned()],
 | 
			
		||||
                    func: Some(mem_func),
 | 
			
		||||
                    memory: Box::new(memory.clone()),
 | 
			
		||||
                    func: None,
 | 
			
		||||
                    memory: Box::new(exec_state.memory.clone()),
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            Expr::CallExpression(call_expression) => {
 | 
			
		||||
                call_expression.execute(memory, dynamic_state, pipe_info, self).await?
 | 
			
		||||
            }
 | 
			
		||||
            Expr::PipeExpression(pipe_expression) => {
 | 
			
		||||
                pipe_expression
 | 
			
		||||
                    .get_result(memory, dynamic_state, pipe_info, self)
 | 
			
		||||
                    .await?
 | 
			
		||||
            }
 | 
			
		||||
            Expr::CallExpression(call_expression) => call_expression.execute(exec_state, self).await?,
 | 
			
		||||
            Expr::PipeExpression(pipe_expression) => pipe_expression.get_result(exec_state, self).await?,
 | 
			
		||||
            Expr::PipeSubstitution(pipe_substitution) => match statement_kind {
 | 
			
		||||
                StatementKind::Declaration { name } => {
 | 
			
		||||
                    let message = format!(
 | 
			
		||||
@ -1973,7 +1903,7 @@ impl ExecutorContext {
 | 
			
		||||
                        source_ranges: vec![pipe_substitution.into()],
 | 
			
		||||
                    }));
 | 
			
		||||
                }
 | 
			
		||||
                StatementKind::Expression => match pipe_info.previous_results.clone() {
 | 
			
		||||
                StatementKind::Expression => match exec_state.pipe_value.clone() {
 | 
			
		||||
                    Some(x) => x,
 | 
			
		||||
                    None => {
 | 
			
		||||
                        return Err(KclError::Semantic(KclErrorDetails {
 | 
			
		||||
@ -1983,20 +1913,10 @@ impl ExecutorContext {
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
            },
 | 
			
		||||
            Expr::ArrayExpression(array_expression) => {
 | 
			
		||||
                array_expression.execute(memory, dynamic_state, pipe_info, self).await?
 | 
			
		||||
            }
 | 
			
		||||
            Expr::ObjectExpression(object_expression) => {
 | 
			
		||||
                object_expression
 | 
			
		||||
                    .execute(memory, dynamic_state, pipe_info, self)
 | 
			
		||||
                    .await?
 | 
			
		||||
            }
 | 
			
		||||
            Expr::MemberExpression(member_expression) => member_expression.get_result(memory)?,
 | 
			
		||||
            Expr::UnaryExpression(unary_expression) => {
 | 
			
		||||
                unary_expression
 | 
			
		||||
                    .get_result(memory, dynamic_state, pipe_info, self)
 | 
			
		||||
                    .await?
 | 
			
		||||
            }
 | 
			
		||||
            Expr::ArrayExpression(array_expression) => array_expression.execute(exec_state, self).await?,
 | 
			
		||||
            Expr::ObjectExpression(object_expression) => object_expression.execute(exec_state, self).await?,
 | 
			
		||||
            Expr::MemberExpression(member_expression) => member_expression.get_result(exec_state)?,
 | 
			
		||||
            Expr::UnaryExpression(unary_expression) => unary_expression.get_result(exec_state, self).await?,
 | 
			
		||||
        };
 | 
			
		||||
        Ok(item)
 | 
			
		||||
    }
 | 
			
		||||
@ -2097,6 +2017,36 @@ fn assign_args_to_params(
 | 
			
		||||
    Ok(fn_memory)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) async fn call_user_defined_function(
 | 
			
		||||
    args: Vec<KclValue>,
 | 
			
		||||
    memory: &ProgramMemory,
 | 
			
		||||
    function_expression: &FunctionExpression,
 | 
			
		||||
    exec_state: &mut ExecState,
 | 
			
		||||
    ctx: &ExecutorContext,
 | 
			
		||||
) -> Result<Option<KclValue>, KclError> {
 | 
			
		||||
    // Create a new environment to execute the function body in so that local
 | 
			
		||||
    // variables shadow variables in the parent scope.  The new environment's
 | 
			
		||||
    // parent should be the environment of the closure.
 | 
			
		||||
    let mut body_memory = memory.clone();
 | 
			
		||||
    let body_env = body_memory.new_env_for_call(memory.current_env);
 | 
			
		||||
    body_memory.current_env = body_env;
 | 
			
		||||
    let fn_memory = assign_args_to_params(function_expression, args, body_memory)?;
 | 
			
		||||
 | 
			
		||||
    // Execute the function body using the memory we just created.
 | 
			
		||||
    let (result, fn_memory) = {
 | 
			
		||||
        let previous_memory = std::mem::replace(&mut exec_state.memory, fn_memory);
 | 
			
		||||
        let result = ctx
 | 
			
		||||
            .inner_execute(&function_expression.body, exec_state, BodyType::Block)
 | 
			
		||||
            .await;
 | 
			
		||||
        // Restore the previous memory.
 | 
			
		||||
        let fn_memory = std::mem::replace(&mut exec_state.memory, previous_memory);
 | 
			
		||||
 | 
			
		||||
        (result, fn_memory)
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    result.map(|()| fn_memory.return_)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub enum StatementKind<'a> {
 | 
			
		||||
    Declaration { name: &'a str },
 | 
			
		||||
    Expression,
 | 
			
		||||
@ -2122,9 +2072,9 @@ mod tests {
 | 
			
		||||
            settings: Default::default(),
 | 
			
		||||
            is_mock: true,
 | 
			
		||||
        };
 | 
			
		||||
        let memory = ctx.run(&program, None).await?;
 | 
			
		||||
        let exec_state = ctx.run(&program, None).await?;
 | 
			
		||||
 | 
			
		||||
        Ok(memory)
 | 
			
		||||
        Ok(exec_state.memory)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Convenience function to get a JSON value from memory and unwrap.
 | 
			
		||||
 | 
			
		||||
@ -3,30 +3,36 @@ use schemars::JsonSchema;
 | 
			
		||||
use crate::{
 | 
			
		||||
    ast::types::FunctionExpression,
 | 
			
		||||
    errors::KclError,
 | 
			
		||||
    executor::{DynamicState, ExecutorContext, KclValue, MemoryFunction, Metadata, ProgramMemory},
 | 
			
		||||
    executor::{
 | 
			
		||||
        call_user_defined_function, ExecState, ExecutorContext, KclValue, MemoryFunction, Metadata, ProgramMemory,
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// A function being used as a parameter into a stdlib function.
 | 
			
		||||
/// A function being used as a parameter into a stdlib function.  This is a
 | 
			
		||||
/// closure, plus everything needed to execute it.
 | 
			
		||||
pub struct FunctionParam<'a> {
 | 
			
		||||
    pub inner: &'a MemoryFunction,
 | 
			
		||||
    pub inner: Option<&'a MemoryFunction>,
 | 
			
		||||
    pub memory: ProgramMemory,
 | 
			
		||||
    pub dynamic_state: DynamicState,
 | 
			
		||||
    pub fn_expr: Box<FunctionExpression>,
 | 
			
		||||
    pub meta: Vec<Metadata>,
 | 
			
		||||
    pub ctx: ExecutorContext,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> FunctionParam<'a> {
 | 
			
		||||
    pub async fn call(&self, args: Vec<KclValue>) -> Result<Option<KclValue>, KclError> {
 | 
			
		||||
        (self.inner)(
 | 
			
		||||
            args,
 | 
			
		||||
            self.memory.clone(),
 | 
			
		||||
            self.fn_expr.clone(),
 | 
			
		||||
            self.meta.clone(),
 | 
			
		||||
            self.dynamic_state.clone(),
 | 
			
		||||
            self.ctx.clone(),
 | 
			
		||||
        )
 | 
			
		||||
        .await
 | 
			
		||||
    pub async fn call(&self, exec_state: &mut ExecState, args: Vec<KclValue>) -> Result<Option<KclValue>, KclError> {
 | 
			
		||||
        if let Some(inner) = self.inner {
 | 
			
		||||
            inner(
 | 
			
		||||
                args,
 | 
			
		||||
                self.memory.clone(),
 | 
			
		||||
                self.fn_expr.clone(),
 | 
			
		||||
                self.meta.clone(),
 | 
			
		||||
                exec_state,
 | 
			
		||||
                self.ctx.clone(),
 | 
			
		||||
            )
 | 
			
		||||
            .await
 | 
			
		||||
        } else {
 | 
			
		||||
            call_user_defined_function(args, &self.memory, self.fn_expr.as_ref(), exec_state, &self.ctx).await
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -591,8 +591,8 @@ impl Backend {
 | 
			
		||||
        // Clear the scene, before we execute so it's not fugly as shit.
 | 
			
		||||
        executor_ctx.engine.clear_scene(SourceRange::default()).await?;
 | 
			
		||||
 | 
			
		||||
        let memory = match executor_ctx.run(ast, None).await {
 | 
			
		||||
            Ok(memory) => memory,
 | 
			
		||||
        let exec_state = match executor_ctx.run(ast, None).await {
 | 
			
		||||
            Ok(exec_state) => exec_state,
 | 
			
		||||
            Err(err) => {
 | 
			
		||||
                self.memory_map.remove(params.uri.as_str());
 | 
			
		||||
                self.add_to_diagnostics(params, &[err], false).await;
 | 
			
		||||
@ -603,11 +603,12 @@ impl Backend {
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        self.memory_map.insert(params.uri.to_string(), memory.clone());
 | 
			
		||||
        self.memory_map
 | 
			
		||||
            .insert(params.uri.to_string(), exec_state.memory.clone());
 | 
			
		||||
 | 
			
		||||
        // Send the notification to the client that the memory was updated.
 | 
			
		||||
        self.client
 | 
			
		||||
            .send_notification::<custom_notifications::MemoryUpdated>(memory)
 | 
			
		||||
            .send_notification::<custom_notifications::MemoryUpdated>(exec_state.memory)
 | 
			
		||||
            .await;
 | 
			
		||||
 | 
			
		||||
        Ok(())
 | 
			
		||||
 | 
			
		||||
@ -8,8 +8,8 @@ use crate::{
 | 
			
		||||
    ast::types::{parse_json_number_as_f64, TagDeclarator},
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    executor::{
 | 
			
		||||
        DynamicState, ExecutorContext, ExtrudeGroup, ExtrudeGroupSet, ExtrudeSurface, KclValue, Metadata,
 | 
			
		||||
        ProgramMemory, SketchGroup, SketchGroupSet, SketchSurface, SourceRange, TagIdentifier,
 | 
			
		||||
        ExecState, ExecutorContext, ExtrudeGroup, ExtrudeGroupSet, ExtrudeSurface, KclValue, Metadata, SketchGroup,
 | 
			
		||||
        SketchGroupSet, SketchSurface, SourceRange, TagIdentifier,
 | 
			
		||||
    },
 | 
			
		||||
    std::{shapes::SketchSurfaceOrGroup, sketch::FaceTag, FnAsArg},
 | 
			
		||||
};
 | 
			
		||||
@ -19,24 +19,14 @@ pub struct Args {
 | 
			
		||||
    pub args: Vec<KclValue>,
 | 
			
		||||
    pub source_range: SourceRange,
 | 
			
		||||
    pub ctx: ExecutorContext,
 | 
			
		||||
    pub current_program_memory: ProgramMemory,
 | 
			
		||||
    pub dynamic_state: DynamicState,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Args {
 | 
			
		||||
    pub fn new(
 | 
			
		||||
        args: Vec<KclValue>,
 | 
			
		||||
        source_range: SourceRange,
 | 
			
		||||
        ctx: ExecutorContext,
 | 
			
		||||
        current_program_memory: ProgramMemory,
 | 
			
		||||
        dynamic_state: DynamicState,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
    pub fn new(args: Vec<KclValue>, source_range: SourceRange, ctx: ExecutorContext) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            args,
 | 
			
		||||
            source_range,
 | 
			
		||||
            ctx,
 | 
			
		||||
            current_program_memory,
 | 
			
		||||
            dynamic_state,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -54,8 +44,6 @@ impl Args {
 | 
			
		||||
                settings: Default::default(),
 | 
			
		||||
                is_mock: true,
 | 
			
		||||
            },
 | 
			
		||||
            current_program_memory: ProgramMemory::default(),
 | 
			
		||||
            dynamic_state: DynamicState::default(),
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -88,11 +76,12 @@ impl Args {
 | 
			
		||||
        self.ctx.engine.send_modeling_cmd(id, self.source_range, cmd).await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn get_tag_info_from_memory<'a>(
 | 
			
		||||
    fn get_tag_info_from_memory<'a, 'e>(
 | 
			
		||||
        &'a self,
 | 
			
		||||
        exec_state: &'e mut ExecState,
 | 
			
		||||
        tag: &'a TagIdentifier,
 | 
			
		||||
    ) -> Result<&'a crate::executor::TagEngineInfo, KclError> {
 | 
			
		||||
        if let KclValue::TagIdentifier(t) = self.current_program_memory.get(&tag.value, self.source_range)? {
 | 
			
		||||
    ) -> Result<&'e crate::executor::TagEngineInfo, KclError> {
 | 
			
		||||
        if let KclValue::TagIdentifier(t) = exec_state.memory.get(&tag.value, self.source_range)? {
 | 
			
		||||
            Ok(t.info.as_ref().ok_or_else(|| {
 | 
			
		||||
                KclError::Type(KclErrorDetails {
 | 
			
		||||
                    message: format!("Tag `{}` does not have engine info", tag.value),
 | 
			
		||||
@ -107,34 +96,43 @@ impl Args {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(crate) fn get_tag_engine_info<'a>(
 | 
			
		||||
    pub(crate) fn get_tag_engine_info<'a, 'e>(
 | 
			
		||||
        &'a self,
 | 
			
		||||
        exec_state: &'e mut ExecState,
 | 
			
		||||
        tag: &'a TagIdentifier,
 | 
			
		||||
    ) -> Result<&'a crate::executor::TagEngineInfo, KclError> {
 | 
			
		||||
    ) -> Result<&'a crate::executor::TagEngineInfo, KclError>
 | 
			
		||||
    where
 | 
			
		||||
        'e: 'a,
 | 
			
		||||
    {
 | 
			
		||||
        if let Some(info) = &tag.info {
 | 
			
		||||
            return Ok(info);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        self.get_tag_info_from_memory(tag)
 | 
			
		||||
        self.get_tag_info_from_memory(exec_state, tag)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn get_tag_engine_info_check_surface<'a>(
 | 
			
		||||
    fn get_tag_engine_info_check_surface<'a, 'e>(
 | 
			
		||||
        &'a self,
 | 
			
		||||
        exec_state: &'e mut ExecState,
 | 
			
		||||
        tag: &'a TagIdentifier,
 | 
			
		||||
    ) -> Result<&'a crate::executor::TagEngineInfo, KclError> {
 | 
			
		||||
    ) -> Result<&'a crate::executor::TagEngineInfo, KclError>
 | 
			
		||||
    where
 | 
			
		||||
        'e: 'a,
 | 
			
		||||
    {
 | 
			
		||||
        if let Some(info) = &tag.info {
 | 
			
		||||
            if info.surface.is_some() {
 | 
			
		||||
                return Ok(info);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        self.get_tag_info_from_memory(tag)
 | 
			
		||||
        self.get_tag_info_from_memory(exec_state, tag)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Flush just the fillets and chamfers for this specific ExtrudeGroupSet.
 | 
			
		||||
    #[allow(clippy::vec_box)]
 | 
			
		||||
    pub(crate) async fn flush_batch_for_extrude_group_set(
 | 
			
		||||
        &self,
 | 
			
		||||
        exec_state: &mut ExecState,
 | 
			
		||||
        extrude_groups: Vec<Box<ExtrudeGroup>>,
 | 
			
		||||
    ) -> Result<(), KclError> {
 | 
			
		||||
        // Make sure we don't traverse sketch_groups more than once.
 | 
			
		||||
@ -148,12 +146,13 @@ impl Args {
 | 
			
		||||
            if !traversed_sketch_groups.contains(&sketch_group_id) {
 | 
			
		||||
                // Find all the extrude groups on the same shared sketch group.
 | 
			
		||||
                ids.extend(
 | 
			
		||||
                    self.current_program_memory
 | 
			
		||||
                    exec_state
 | 
			
		||||
                        .memory
 | 
			
		||||
                        .find_extrude_groups_on_sketch_group(extrude_group.sketch_group.id)
 | 
			
		||||
                        .iter()
 | 
			
		||||
                        .flat_map(|eg| eg.get_all_edge_cut_ids()),
 | 
			
		||||
                );
 | 
			
		||||
                ids.extend(self.dynamic_state.edge_cut_ids_on_sketch_group(sketch_group_id));
 | 
			
		||||
                ids.extend(exec_state.dynamic_state.edge_cut_ids_on_sketch_group(sketch_group_id));
 | 
			
		||||
                traversed_sketch_groups.push(sketch_group_id);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@ -380,6 +379,7 @@ impl Args {
 | 
			
		||||
 | 
			
		||||
    pub(crate) async fn get_adjacent_face_to_tag(
 | 
			
		||||
        &self,
 | 
			
		||||
        exec_state: &mut ExecState,
 | 
			
		||||
        tag: &TagIdentifier,
 | 
			
		||||
        must_be_planar: bool,
 | 
			
		||||
    ) -> Result<uuid::Uuid, KclError> {
 | 
			
		||||
@ -390,7 +390,7 @@ impl Args {
 | 
			
		||||
            }));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let engine_info = self.get_tag_engine_info_check_surface(tag)?;
 | 
			
		||||
        let engine_info = self.get_tag_engine_info_check_surface(exec_state, tag)?;
 | 
			
		||||
 | 
			
		||||
        let surface = engine_info.surface.as_ref().ok_or_else(|| {
 | 
			
		||||
            KclError::Type(KclErrorDetails {
 | 
			
		||||
 | 
			
		||||
@ -3,14 +3,14 @@ use schemars::JsonSchema;
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    executor::{KclValue, SketchGroup, SourceRange, UserVal},
 | 
			
		||||
    executor::{ExecState, KclValue, SketchGroup, SourceRange, UserVal},
 | 
			
		||||
    function_param::FunctionParam,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use super::{args::FromArgs, Args, FnAsArg};
 | 
			
		||||
 | 
			
		||||
/// For each item in an array, update a value.
 | 
			
		||||
pub async fn array_reduce(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn array_reduce(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (array, start, f): (Vec<u64>, SketchGroup, FnAsArg<'_>) = FromArgs::from_args(&args, 0)?;
 | 
			
		||||
    let reduce_fn = FunctionParam {
 | 
			
		||||
        inner: f.func,
 | 
			
		||||
@ -18,9 +18,8 @@ pub async fn array_reduce(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
        meta: vec![args.source_range.into()],
 | 
			
		||||
        ctx: args.ctx.clone(),
 | 
			
		||||
        memory: *f.memory,
 | 
			
		||||
        dynamic_state: args.dynamic_state.clone(),
 | 
			
		||||
    };
 | 
			
		||||
    inner_array_reduce(array, start, reduce_fn, &args)
 | 
			
		||||
    inner_array_reduce(array, start, reduce_fn, exec_state, &args)
 | 
			
		||||
        .await
 | 
			
		||||
        .map(|sg| KclValue::UserVal(UserVal::set(sg.meta.clone(), sg)))
 | 
			
		||||
}
 | 
			
		||||
@ -46,11 +45,12 @@ async fn inner_array_reduce<'a>(
 | 
			
		||||
    array: Vec<u64>,
 | 
			
		||||
    start: SketchGroup,
 | 
			
		||||
    reduce_fn: FunctionParam<'a>,
 | 
			
		||||
    exec_state: &mut ExecState,
 | 
			
		||||
    args: &'a Args,
 | 
			
		||||
) -> Result<SketchGroup, KclError> {
 | 
			
		||||
    let mut reduced = start;
 | 
			
		||||
    for i in array {
 | 
			
		||||
        reduced = call_reduce_closure(i, reduced, &reduce_fn, args.source_range).await?;
 | 
			
		||||
        reduced = call_reduce_closure(i, reduced, &reduce_fn, args.source_range, exec_state).await?;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Ok(reduced)
 | 
			
		||||
@ -61,6 +61,7 @@ async fn call_reduce_closure<'a>(
 | 
			
		||||
    start: SketchGroup,
 | 
			
		||||
    reduce_fn: &FunctionParam<'a>,
 | 
			
		||||
    source_range: SourceRange,
 | 
			
		||||
    exec_state: &mut ExecState,
 | 
			
		||||
) -> Result<SketchGroup, KclError> {
 | 
			
		||||
    // Call the reduce fn for this repetition.
 | 
			
		||||
    let reduce_fn_args = vec![
 | 
			
		||||
@ -70,7 +71,7 @@ async fn call_reduce_closure<'a>(
 | 
			
		||||
        }),
 | 
			
		||||
        KclValue::new_user_val(start.meta.clone(), start),
 | 
			
		||||
    ];
 | 
			
		||||
    let transform_fn_return = reduce_fn.call(reduce_fn_args).await?;
 | 
			
		||||
    let transform_fn_return = reduce_fn.call(exec_state, reduce_fn_args).await?;
 | 
			
		||||
 | 
			
		||||
    // Unpack the returned transform object.
 | 
			
		||||
    let source_ranges = vec![source_range];
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,7 @@ use schemars::JsonSchema;
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    executor::KclValue,
 | 
			
		||||
    executor::{ExecState, KclValue},
 | 
			
		||||
    std::Args,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -22,7 +22,7 @@ async fn _assert(value: bool, message: &str, args: &Args) -> Result<(), KclError
 | 
			
		||||
 | 
			
		||||
/// Check that the provided value is true, or raise a [KclError]
 | 
			
		||||
/// with the provided description.
 | 
			
		||||
pub async fn assert(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn assert(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (data, description): (bool, String) = args.get_data()?;
 | 
			
		||||
    inner_assert(data, &description, &args).await?;
 | 
			
		||||
    args.make_null_user_val()
 | 
			
		||||
@ -42,7 +42,7 @@ async fn inner_assert(data: bool, message: &str, args: &Args) -> Result<(), KclE
 | 
			
		||||
    _assert(data, message, args).await
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn assert_lt(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn assert_lt(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (left, right, description): (f64, f64, String) = args.get_data()?;
 | 
			
		||||
    inner_assert_lt(left, right, &description, &args).await?;
 | 
			
		||||
    args.make_null_user_val()
 | 
			
		||||
@ -61,7 +61,7 @@ async fn inner_assert_lt(left: f64, right: f64, message: &str, args: &Args) -> R
 | 
			
		||||
    _assert(left < right, message, args).await
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn assert_gt(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn assert_gt(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (left, right, description): (f64, f64, String) = args.get_data()?;
 | 
			
		||||
    inner_assert_gt(left, right, &description, &args).await?;
 | 
			
		||||
    args.make_null_user_val()
 | 
			
		||||
@ -89,7 +89,7 @@ async fn inner_assert_equal(left: f64, right: f64, epsilon: f64, message: &str,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn assert_equal(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn assert_equal(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (left, right, epsilon, description): (f64, f64, f64, String) = args.get_data()?;
 | 
			
		||||
    inner_assert_equal(left, right, epsilon, &description, &args).await?;
 | 
			
		||||
    args.make_null_user_val()
 | 
			
		||||
@ -108,7 +108,7 @@ async fn inner_assert_gt(left: f64, right: f64, message: &str, args: &Args) -> R
 | 
			
		||||
    _assert(left > right, message, args).await
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn assert_lte(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn assert_lte(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (left, right, description): (f64, f64, String) = args.get_data()?;
 | 
			
		||||
    inner_assert_lte(left, right, &description, &args).await?;
 | 
			
		||||
    args.make_null_user_val()
 | 
			
		||||
@ -128,7 +128,7 @@ async fn inner_assert_lte(left: f64, right: f64, message: &str, args: &Args) ->
 | 
			
		||||
    _assert(left <= right, message, args).await
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn assert_gte(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn assert_gte(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (left, right, description): (f64, f64, String) = args.get_data()?;
 | 
			
		||||
    inner_assert_gte(left, right, &description, &args).await?;
 | 
			
		||||
    args.make_null_user_val()
 | 
			
		||||
 | 
			
		||||
@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize};
 | 
			
		||||
use crate::{
 | 
			
		||||
    ast::types::TagDeclarator,
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    executor::{ChamferSurface, EdgeCut, ExtrudeGroup, ExtrudeSurface, GeoMeta, KclValue},
 | 
			
		||||
    executor::{ChamferSurface, EdgeCut, ExecState, ExtrudeGroup, ExtrudeSurface, GeoMeta, KclValue},
 | 
			
		||||
    std::{fillet::EdgeReference, Args},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -27,11 +27,11 @@ pub struct ChamferData {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Create chamfers on tagged paths.
 | 
			
		||||
pub async fn chamfer(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn chamfer(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (data, extrude_group, tag): (ChamferData, Box<ExtrudeGroup>, Option<TagDeclarator>) =
 | 
			
		||||
        args.get_data_and_extrude_group_and_tag()?;
 | 
			
		||||
 | 
			
		||||
    let extrude_group = inner_chamfer(data, extrude_group, tag, args).await?;
 | 
			
		||||
    let extrude_group = inner_chamfer(data, extrude_group, tag, exec_state, args).await?;
 | 
			
		||||
    Ok(KclValue::ExtrudeGroup(extrude_group))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -103,6 +103,7 @@ async fn inner_chamfer(
 | 
			
		||||
    data: ChamferData,
 | 
			
		||||
    extrude_group: Box<ExtrudeGroup>,
 | 
			
		||||
    tag: Option<TagDeclarator>,
 | 
			
		||||
    exec_state: &mut ExecState,
 | 
			
		||||
    args: Args,
 | 
			
		||||
) -> Result<Box<ExtrudeGroup>, KclError> {
 | 
			
		||||
    // Check if tags contains any duplicate values.
 | 
			
		||||
@ -130,7 +131,7 @@ async fn inner_chamfer(
 | 
			
		||||
    for edge_tag in data.tags {
 | 
			
		||||
        let edge_id = match edge_tag {
 | 
			
		||||
            EdgeReference::Uuid(uuid) => uuid,
 | 
			
		||||
            EdgeReference::Tag(edge_tag) => args.get_tag_engine_info(&edge_tag)?.id,
 | 
			
		||||
            EdgeReference::Tag(edge_tag) => args.get_tag_engine_info(exec_state, &edge_tag)?.id,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        let id = uuid::Uuid::new_v4();
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,7 @@ use schemars::JsonSchema;
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    executor::{KclValue, SourceRange},
 | 
			
		||||
    executor::{ExecState, KclValue, SourceRange},
 | 
			
		||||
    std::Args,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -31,7 +31,7 @@ impl ConversionError {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Converts a number to integer.
 | 
			
		||||
pub async fn int(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn int(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let num = args.get_number()?;
 | 
			
		||||
    let converted = inner_int(num).map_err(|err| err.into_kcl_error(args.source_range))?;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -11,14 +11,14 @@ use uuid::Uuid;
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    executor::{
 | 
			
		||||
        ExtrudeGroup, ExtrudeGroupSet, ExtrudeSurface, GeoMeta, KclValue, Path, SketchGroup, SketchGroupSet,
 | 
			
		||||
        ExecState, ExtrudeGroup, ExtrudeGroupSet, ExtrudeSurface, GeoMeta, KclValue, Path, SketchGroup, SketchGroupSet,
 | 
			
		||||
        SketchSurface,
 | 
			
		||||
    },
 | 
			
		||||
    std::Args,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Extrudes by a given amount.
 | 
			
		||||
pub async fn extrude(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn extrude(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (length, sketch_group_set) = args.get_number_sketch_group_set()?;
 | 
			
		||||
 | 
			
		||||
    let result = inner_extrude(length, sketch_group_set, args).await?;
 | 
			
		||||
 | 
			
		||||
@ -10,7 +10,9 @@ use uuid::Uuid;
 | 
			
		||||
use crate::{
 | 
			
		||||
    ast::types::TagDeclarator,
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    executor::{EdgeCut, ExtrudeGroup, ExtrudeSurface, FilletSurface, GeoMeta, KclValue, TagIdentifier, UserVal},
 | 
			
		||||
    executor::{
 | 
			
		||||
        EdgeCut, ExecState, ExtrudeGroup, ExtrudeSurface, FilletSurface, GeoMeta, KclValue, TagIdentifier, UserVal,
 | 
			
		||||
    },
 | 
			
		||||
    settings::types::UnitLength,
 | 
			
		||||
    std::Args,
 | 
			
		||||
};
 | 
			
		||||
@ -41,11 +43,11 @@ pub enum EdgeReference {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Create fillets on tagged paths.
 | 
			
		||||
pub async fn fillet(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn fillet(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (data, extrude_group, tag): (FilletData, Box<ExtrudeGroup>, Option<TagDeclarator>) =
 | 
			
		||||
        args.get_data_and_extrude_group_and_tag()?;
 | 
			
		||||
 | 
			
		||||
    let extrude_group = inner_fillet(data, extrude_group, tag, args).await?;
 | 
			
		||||
    let extrude_group = inner_fillet(data, extrude_group, tag, exec_state, args).await?;
 | 
			
		||||
    Ok(KclValue::ExtrudeGroup(extrude_group))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -112,6 +114,7 @@ async fn inner_fillet(
 | 
			
		||||
    data: FilletData,
 | 
			
		||||
    extrude_group: Box<ExtrudeGroup>,
 | 
			
		||||
    tag: Option<TagDeclarator>,
 | 
			
		||||
    exec_state: &mut ExecState,
 | 
			
		||||
    args: Args,
 | 
			
		||||
) -> Result<Box<ExtrudeGroup>, KclError> {
 | 
			
		||||
    // Check if tags contains any duplicate values.
 | 
			
		||||
@ -130,7 +133,7 @@ async fn inner_fillet(
 | 
			
		||||
    for edge_tag in data.tags {
 | 
			
		||||
        let edge_id = match edge_tag {
 | 
			
		||||
            EdgeReference::Uuid(uuid) => uuid,
 | 
			
		||||
            EdgeReference::Tag(edge_tag) => args.get_tag_engine_info(&edge_tag)?.id,
 | 
			
		||||
            EdgeReference::Tag(edge_tag) => args.get_tag_engine_info(exec_state, &edge_tag)?.id,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        let id = uuid::Uuid::new_v4();
 | 
			
		||||
@ -172,10 +175,10 @@ async fn inner_fillet(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Get the opposite edge to the edge given.
 | 
			
		||||
pub async fn get_opposite_edge(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn get_opposite_edge(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let tag: TagIdentifier = args.get_data()?;
 | 
			
		||||
 | 
			
		||||
    let edge = inner_get_opposite_edge(tag, args.clone()).await?;
 | 
			
		||||
    let edge = inner_get_opposite_edge(tag, exec_state, args.clone()).await?;
 | 
			
		||||
    Ok(KclValue::UserVal(UserVal {
 | 
			
		||||
        value: serde_json::to_value(edge).map_err(|e| {
 | 
			
		||||
            KclError::Type(KclErrorDetails {
 | 
			
		||||
@ -217,13 +220,13 @@ pub async fn get_opposite_edge(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
#[stdlib {
 | 
			
		||||
    name = "getOppositeEdge",
 | 
			
		||||
}]
 | 
			
		||||
async fn inner_get_opposite_edge(tag: TagIdentifier, args: Args) -> Result<Uuid, KclError> {
 | 
			
		||||
async fn inner_get_opposite_edge(tag: TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<Uuid, KclError> {
 | 
			
		||||
    if args.ctx.is_mock {
 | 
			
		||||
        return Ok(Uuid::new_v4());
 | 
			
		||||
    }
 | 
			
		||||
    let tagged_path = args.get_tag_engine_info(&tag)?;
 | 
			
		||||
    let face_id = args.get_adjacent_face_to_tag(exec_state, &tag, false).await?;
 | 
			
		||||
 | 
			
		||||
    let face_id = args.get_adjacent_face_to_tag(&tag, false).await?;
 | 
			
		||||
    let tagged_path = args.get_tag_engine_info(exec_state, &tag)?;
 | 
			
		||||
 | 
			
		||||
    let resp = args
 | 
			
		||||
        .send_modeling_cmd(
 | 
			
		||||
@ -249,10 +252,10 @@ async fn inner_get_opposite_edge(tag: TagIdentifier, args: Args) -> Result<Uuid,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Get the next adjacent edge to the edge given.
 | 
			
		||||
pub async fn get_next_adjacent_edge(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn get_next_adjacent_edge(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let tag: TagIdentifier = args.get_data()?;
 | 
			
		||||
 | 
			
		||||
    let edge = inner_get_next_adjacent_edge(tag, args.clone()).await?;
 | 
			
		||||
    let edge = inner_get_next_adjacent_edge(tag, exec_state, args.clone()).await?;
 | 
			
		||||
    Ok(KclValue::UserVal(UserVal {
 | 
			
		||||
        value: serde_json::to_value(edge).map_err(|e| {
 | 
			
		||||
            KclError::Type(KclErrorDetails {
 | 
			
		||||
@ -294,13 +297,17 @@ pub async fn get_next_adjacent_edge(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
#[stdlib {
 | 
			
		||||
    name = "getNextAdjacentEdge",
 | 
			
		||||
}]
 | 
			
		||||
async fn inner_get_next_adjacent_edge(tag: TagIdentifier, args: Args) -> Result<Uuid, KclError> {
 | 
			
		||||
async fn inner_get_next_adjacent_edge(
 | 
			
		||||
    tag: TagIdentifier,
 | 
			
		||||
    exec_state: &mut ExecState,
 | 
			
		||||
    args: Args,
 | 
			
		||||
) -> Result<Uuid, KclError> {
 | 
			
		||||
    if args.ctx.is_mock {
 | 
			
		||||
        return Ok(Uuid::new_v4());
 | 
			
		||||
    }
 | 
			
		||||
    let tagged_path = args.get_tag_engine_info(&tag)?;
 | 
			
		||||
    let face_id = args.get_adjacent_face_to_tag(exec_state, &tag, false).await?;
 | 
			
		||||
 | 
			
		||||
    let face_id = args.get_adjacent_face_to_tag(&tag, false).await?;
 | 
			
		||||
    let tagged_path = args.get_tag_engine_info(exec_state, &tag)?;
 | 
			
		||||
 | 
			
		||||
    let resp = args
 | 
			
		||||
        .send_modeling_cmd(
 | 
			
		||||
@ -331,10 +338,10 @@ async fn inner_get_next_adjacent_edge(tag: TagIdentifier, args: Args) -> Result<
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Get the previous adjacent edge to the edge given.
 | 
			
		||||
pub async fn get_previous_adjacent_edge(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn get_previous_adjacent_edge(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let tag: TagIdentifier = args.get_data()?;
 | 
			
		||||
 | 
			
		||||
    let edge = inner_get_previous_adjacent_edge(tag, args.clone()).await?;
 | 
			
		||||
    let edge = inner_get_previous_adjacent_edge(tag, exec_state, args.clone()).await?;
 | 
			
		||||
    Ok(KclValue::UserVal(UserVal {
 | 
			
		||||
        value: serde_json::to_value(edge).map_err(|e| {
 | 
			
		||||
            KclError::Type(KclErrorDetails {
 | 
			
		||||
@ -376,13 +383,17 @@ pub async fn get_previous_adjacent_edge(args: Args) -> Result<KclValue, KclError
 | 
			
		||||
#[stdlib {
 | 
			
		||||
    name = "getPreviousAdjacentEdge",
 | 
			
		||||
}]
 | 
			
		||||
async fn inner_get_previous_adjacent_edge(tag: TagIdentifier, args: Args) -> Result<Uuid, KclError> {
 | 
			
		||||
async fn inner_get_previous_adjacent_edge(
 | 
			
		||||
    tag: TagIdentifier,
 | 
			
		||||
    exec_state: &mut ExecState,
 | 
			
		||||
    args: Args,
 | 
			
		||||
) -> Result<Uuid, KclError> {
 | 
			
		||||
    if args.ctx.is_mock {
 | 
			
		||||
        return Ok(Uuid::new_v4());
 | 
			
		||||
    }
 | 
			
		||||
    let tagged_path = args.get_tag_engine_info(&tag)?;
 | 
			
		||||
    let face_id = args.get_adjacent_face_to_tag(exec_state, &tag, false).await?;
 | 
			
		||||
 | 
			
		||||
    let face_id = args.get_adjacent_face_to_tag(&tag, false).await?;
 | 
			
		||||
    let tagged_path = args.get_tag_engine_info(exec_state, &tag)?;
 | 
			
		||||
 | 
			
		||||
    let resp = args
 | 
			
		||||
        .send_modeling_cmd(
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::KclError,
 | 
			
		||||
    executor::{ExtrudeGroup, KclValue},
 | 
			
		||||
    executor::{ExecState, ExtrudeGroup, KclValue},
 | 
			
		||||
    std::Args,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -31,7 +31,7 @@ pub struct HelixData {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Create a helix on a cylinder.
 | 
			
		||||
pub async fn helix(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn helix(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (data, extrude_group): (HelixData, Box<ExtrudeGroup>) = args.get_data_and_extrude_group()?;
 | 
			
		||||
 | 
			
		||||
    let extrude_group = inner_helix(data, extrude_group, args).await?;
 | 
			
		||||
 | 
			
		||||
@ -9,7 +9,7 @@ use schemars::JsonSchema;
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    executor::{ImportedGeometry, KclValue},
 | 
			
		||||
    executor::{ExecState, ImportedGeometry, KclValue},
 | 
			
		||||
    fs::FileSystem,
 | 
			
		||||
    std::Args,
 | 
			
		||||
};
 | 
			
		||||
@ -117,7 +117,7 @@ impl From<ImportFormat> for kittycad::types::InputFormat {
 | 
			
		||||
///
 | 
			
		||||
/// Import paths are relative to the current project directory. This only works in the desktop app
 | 
			
		||||
/// not in browser.
 | 
			
		||||
pub async fn import(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn import(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (file_path, options): (String, Option<ImportFormat>) = args.get_import_data()?;
 | 
			
		||||
 | 
			
		||||
    let imported_geometry = inner_import(file_path, options, args).await?;
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    executor::{ExtrudeGroup, KclValue, SketchGroup},
 | 
			
		||||
    executor::{ExecState, ExtrudeGroup, KclValue, SketchGroup},
 | 
			
		||||
    std::{extrude::do_post_extrude, fillet::default_tolerance, Args},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -49,7 +49,7 @@ impl Default for LoftData {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Create a 3D surface or solid by interpolating between two or more sketches.
 | 
			
		||||
pub async fn loft(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn loft(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (sketch_groups, data): (Vec<SketchGroup>, Option<LoftData>) = args.get_sketch_groups_and_data()?;
 | 
			
		||||
 | 
			
		||||
    let extrude_group = inner_loft(sketch_groups, data, args).await?;
 | 
			
		||||
 | 
			
		||||
@ -6,12 +6,12 @@ use schemars::JsonSchema;
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    executor::KclValue,
 | 
			
		||||
    executor::{ExecState, KclValue},
 | 
			
		||||
    std::Args,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Compute the cosine of a number (in radians).
 | 
			
		||||
pub async fn cos(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn cos(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let num = args.get_number()?;
 | 
			
		||||
    let result = inner_cos(num)?;
 | 
			
		||||
 | 
			
		||||
@ -41,7 +41,7 @@ fn inner_cos(num: f64) -> Result<f64, KclError> {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Compute the sine of a number (in radians).
 | 
			
		||||
pub async fn sin(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn sin(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let num = args.get_number()?;
 | 
			
		||||
    let result = inner_sin(num)?;
 | 
			
		||||
 | 
			
		||||
@ -71,7 +71,7 @@ fn inner_sin(num: f64) -> Result<f64, KclError> {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Compute the tangent of a number (in radians).
 | 
			
		||||
pub async fn tan(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn tan(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let num = args.get_number()?;
 | 
			
		||||
    let result = inner_tan(num)?;
 | 
			
		||||
 | 
			
		||||
@ -101,7 +101,7 @@ fn inner_tan(num: f64) -> Result<f64, KclError> {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Return the value of `pi`. Archimedes’ constant (π).
 | 
			
		||||
pub async fn pi(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn pi(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let result = inner_pi()?;
 | 
			
		||||
 | 
			
		||||
    args.make_user_val_from_f64(result)
 | 
			
		||||
@ -126,7 +126,7 @@ fn inner_pi() -> Result<f64, KclError> {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Compute the square root of a number.
 | 
			
		||||
pub async fn sqrt(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn sqrt(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let num = args.get_number()?;
 | 
			
		||||
    let result = inner_sqrt(num)?;
 | 
			
		||||
 | 
			
		||||
@ -156,7 +156,7 @@ fn inner_sqrt(num: f64) -> Result<f64, KclError> {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Compute the absolute value of a number.
 | 
			
		||||
pub async fn abs(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn abs(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let num = args.get_number()?;
 | 
			
		||||
    let result = inner_abs(num)?;
 | 
			
		||||
 | 
			
		||||
@ -193,7 +193,7 @@ fn inner_abs(num: f64) -> Result<f64, KclError> {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Compute the largest integer less than or equal to a number.
 | 
			
		||||
pub async fn floor(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn floor(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let num = args.get_number()?;
 | 
			
		||||
    let result = inner_floor(num)?;
 | 
			
		||||
 | 
			
		||||
@ -221,7 +221,7 @@ fn inner_floor(num: f64) -> Result<f64, KclError> {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Compute the smallest integer greater than or equal to a number.
 | 
			
		||||
pub async fn ceil(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn ceil(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let num = args.get_number()?;
 | 
			
		||||
    let result = inner_ceil(num)?;
 | 
			
		||||
 | 
			
		||||
@ -249,7 +249,7 @@ fn inner_ceil(num: f64) -> Result<f64, KclError> {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Compute the minimum of the given arguments.
 | 
			
		||||
pub async fn min(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn min(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let nums = args.get_number_array()?;
 | 
			
		||||
    let result = inner_min(nums);
 | 
			
		||||
 | 
			
		||||
@ -286,7 +286,7 @@ fn inner_min(args: Vec<f64>) -> f64 {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Compute the maximum of the given arguments.
 | 
			
		||||
pub async fn max(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn max(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let nums = args.get_number_array()?;
 | 
			
		||||
    let result = inner_max(nums);
 | 
			
		||||
 | 
			
		||||
@ -323,7 +323,7 @@ fn inner_max(args: Vec<f64>) -> f64 {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Compute the number to a power.
 | 
			
		||||
pub async fn pow(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn pow(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let nums = args.get_number_array()?;
 | 
			
		||||
    if nums.len() > 2 {
 | 
			
		||||
        return Err(KclError::Type(KclErrorDetails {
 | 
			
		||||
@ -367,7 +367,7 @@ fn inner_pow(num: f64, pow: f64) -> Result<f64, KclError> {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Compute the arccosine of a number (in radians).
 | 
			
		||||
pub async fn acos(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn acos(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let num = args.get_number()?;
 | 
			
		||||
    let result = inner_acos(num)?;
 | 
			
		||||
 | 
			
		||||
@ -398,7 +398,7 @@ fn inner_acos(num: f64) -> Result<f64, KclError> {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Compute the arcsine of a number (in radians).
 | 
			
		||||
pub async fn asin(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn asin(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let num = args.get_number()?;
 | 
			
		||||
    let result = inner_asin(num)?;
 | 
			
		||||
 | 
			
		||||
@ -428,7 +428,7 @@ fn inner_asin(num: f64) -> Result<f64, KclError> {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Compute the arctangent of a number (in radians).
 | 
			
		||||
pub async fn atan(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn atan(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let num = args.get_number()?;
 | 
			
		||||
    let result = inner_atan(num)?;
 | 
			
		||||
 | 
			
		||||
@ -462,7 +462,7 @@ fn inner_atan(num: f64) -> Result<f64, KclError> {
 | 
			
		||||
/// The result might not be correctly rounded owing to implementation
 | 
			
		||||
/// details; `log2()` can produce more accurate results for base 2,
 | 
			
		||||
/// and `log10()` can produce more accurate results for base 10.
 | 
			
		||||
pub async fn log(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn log(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let nums = args.get_number_array()?;
 | 
			
		||||
    if nums.len() > 2 {
 | 
			
		||||
        return Err(KclError::Type(KclErrorDetails {
 | 
			
		||||
@ -507,7 +507,7 @@ fn inner_log(num: f64, base: f64) -> Result<f64, KclError> {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Compute the base 2 logarithm of the number.
 | 
			
		||||
pub async fn log2(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn log2(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let num = args.get_number()?;
 | 
			
		||||
    let result = inner_log2(num)?;
 | 
			
		||||
 | 
			
		||||
@ -535,7 +535,7 @@ fn inner_log2(num: f64) -> Result<f64, KclError> {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Compute the base 10 logarithm of the number.
 | 
			
		||||
pub async fn log10(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn log10(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let num = args.get_number()?;
 | 
			
		||||
    let result = inner_log10(num)?;
 | 
			
		||||
 | 
			
		||||
@ -563,7 +563,7 @@ fn inner_log10(num: f64) -> Result<f64, KclError> {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Compute the natural logarithm of the number.
 | 
			
		||||
pub async fn ln(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn ln(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let num = args.get_number()?;
 | 
			
		||||
    let result = inner_ln(num)?;
 | 
			
		||||
 | 
			
		||||
@ -591,7 +591,7 @@ fn inner_ln(num: f64) -> Result<f64, KclError> {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Return the value of Euler’s number `e`.
 | 
			
		||||
pub async fn e(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn e(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let result = inner_e()?;
 | 
			
		||||
 | 
			
		||||
    args.make_user_val_from_f64(result)
 | 
			
		||||
@ -620,7 +620,7 @@ fn inner_e() -> Result<f64, KclError> {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Return the value of `tau`. The full circle constant (τ). Equal to 2π.
 | 
			
		||||
pub async fn tau(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn tau(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let result = inner_tau()?;
 | 
			
		||||
 | 
			
		||||
    args.make_user_val_from_f64(result)
 | 
			
		||||
@ -649,7 +649,7 @@ fn inner_tau() -> Result<f64, KclError> {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Converts a number from degrees to radians.
 | 
			
		||||
pub async fn to_radians(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn to_radians(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let num = args.get_number()?;
 | 
			
		||||
    let result = inner_to_radians(num)?;
 | 
			
		||||
 | 
			
		||||
@ -679,7 +679,7 @@ fn inner_to_radians(num: f64) -> Result<f64, KclError> {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Converts a number from radians to degrees.
 | 
			
		||||
pub async fn to_degrees(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn to_degrees(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let num = args.get_number()?;
 | 
			
		||||
    let result = inner_to_degrees(num)?;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -38,11 +38,14 @@ use crate::{
 | 
			
		||||
    ast::types::FunctionExpression,
 | 
			
		||||
    docs::StdLibFn,
 | 
			
		||||
    errors::KclError,
 | 
			
		||||
    executor::{KclValue, ProgramMemory, SketchGroup, SketchSurface},
 | 
			
		||||
    executor::{ExecState, KclValue, ProgramMemory, SketchGroup, SketchSurface},
 | 
			
		||||
    std::kcl_stdlib::KclStdLibFn,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
pub type StdFn = fn(Args) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<KclValue, KclError>> + Send>>;
 | 
			
		||||
pub type StdFn = fn(
 | 
			
		||||
    &mut ExecState,
 | 
			
		||||
    Args,
 | 
			
		||||
) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<KclValue, KclError>> + Send + '_>>;
 | 
			
		||||
 | 
			
		||||
pub type FnMap = HashMap<String, StdFn>;
 | 
			
		||||
 | 
			
		||||
@ -228,7 +231,7 @@ pub enum FunctionKind {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Compute the length of the given leg.
 | 
			
		||||
pub async fn leg_length(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn leg_length(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (hypotenuse, leg) = args.get_hypotenuse_leg()?;
 | 
			
		||||
    let result = inner_leg_length(hypotenuse, leg);
 | 
			
		||||
    args.make_user_val_from_f64(result)
 | 
			
		||||
@ -248,7 +251,7 @@ fn inner_leg_length(hypotenuse: f64, leg: f64) -> f64 {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Compute the angle of the given leg for x.
 | 
			
		||||
pub async fn leg_angle_x(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn leg_angle_x(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (hypotenuse, leg) = args.get_hypotenuse_leg()?;
 | 
			
		||||
    let result = inner_leg_angle_x(hypotenuse, leg);
 | 
			
		||||
    args.make_user_val_from_f64(result)
 | 
			
		||||
@ -268,7 +271,7 @@ fn inner_leg_angle_x(hypotenuse: f64, leg: f64) -> f64 {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Compute the angle of the given leg for y.
 | 
			
		||||
pub async fn leg_angle_y(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn leg_angle_y(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (hypotenuse, leg) = args.get_hypotenuse_leg()?;
 | 
			
		||||
    let result = inner_leg_angle_y(hypotenuse, leg);
 | 
			
		||||
    args.make_user_val_from_f64(result)
 | 
			
		||||
@ -302,8 +305,9 @@ pub enum Primitive {
 | 
			
		||||
    Uuid,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// A closure used as an argument to a stdlib function.
 | 
			
		||||
pub struct FnAsArg<'a> {
 | 
			
		||||
    pub func: &'a crate::executor::MemoryFunction,
 | 
			
		||||
    pub func: Option<&'a crate::executor::MemoryFunction>,
 | 
			
		||||
    pub expr: Box<FunctionExpression>,
 | 
			
		||||
    pub memory: Box<ProgramMemory>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize};
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    executor::{
 | 
			
		||||
        ExtrudeGroup, ExtrudeGroupSet, Geometries, Geometry, KclValue, Point3d, SketchGroup, SketchGroupSet,
 | 
			
		||||
        ExecState, ExtrudeGroup, ExtrudeGroupSet, Geometries, Geometry, KclValue, Point3d, SketchGroup, SketchGroupSet,
 | 
			
		||||
        SourceRange, UserVal,
 | 
			
		||||
    },
 | 
			
		||||
    function_param::FunctionParam,
 | 
			
		||||
@ -77,7 +77,7 @@ impl LinearPattern {
 | 
			
		||||
/// A linear pattern
 | 
			
		||||
/// Each element in the pattern repeats a particular piece of geometry.
 | 
			
		||||
/// The repetitions can be transformed by the `transform` parameter.
 | 
			
		||||
pub async fn pattern_transform(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn pattern_transform(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (num_repetitions, transform, extr) = args.get_pattern_transform_args()?;
 | 
			
		||||
 | 
			
		||||
    let extrude_groups = inner_pattern_transform(
 | 
			
		||||
@ -88,9 +88,9 @@ pub async fn pattern_transform(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
            meta: vec![args.source_range.into()],
 | 
			
		||||
            ctx: args.ctx.clone(),
 | 
			
		||||
            memory: *transform.memory,
 | 
			
		||||
            dynamic_state: args.dynamic_state.clone(),
 | 
			
		||||
        },
 | 
			
		||||
        extr,
 | 
			
		||||
        exec_state,
 | 
			
		||||
        &args,
 | 
			
		||||
    )
 | 
			
		||||
    .await?;
 | 
			
		||||
@ -131,18 +131,19 @@ async fn inner_pattern_transform<'a>(
 | 
			
		||||
    num_repetitions: u32,
 | 
			
		||||
    transform_function: FunctionParam<'a>,
 | 
			
		||||
    extrude_group_set: ExtrudeGroupSet,
 | 
			
		||||
    exec_state: &mut ExecState,
 | 
			
		||||
    args: &'a Args,
 | 
			
		||||
) -> Result<Vec<Box<ExtrudeGroup>>, KclError> {
 | 
			
		||||
    // Build the vec of transforms, one for each repetition.
 | 
			
		||||
    let mut transform = Vec::with_capacity(usize::try_from(num_repetitions).unwrap());
 | 
			
		||||
    for i in 0..num_repetitions {
 | 
			
		||||
        let t = make_transform(i, &transform_function, args.source_range).await?;
 | 
			
		||||
        let t = make_transform(i, &transform_function, args.source_range, exec_state).await?;
 | 
			
		||||
        transform.push(t);
 | 
			
		||||
    }
 | 
			
		||||
    // Flush the batch for our fillets/chamfers if there are any.
 | 
			
		||||
    // If we do not flush these, then you won't be able to pattern something with fillets.
 | 
			
		||||
    // Flush just the fillets/chamfers that apply to these extrude groups.
 | 
			
		||||
    args.flush_batch_for_extrude_group_set(extrude_group_set.clone().into())
 | 
			
		||||
    args.flush_batch_for_extrude_group_set(exec_state, extrude_group_set.clone().into())
 | 
			
		||||
        .await?;
 | 
			
		||||
 | 
			
		||||
    let starting_extrude_groups: Vec<Box<ExtrudeGroup>> = extrude_group_set.into();
 | 
			
		||||
@ -201,6 +202,7 @@ async fn make_transform<'a>(
 | 
			
		||||
    i: u32,
 | 
			
		||||
    transform_function: &FunctionParam<'a>,
 | 
			
		||||
    source_range: SourceRange,
 | 
			
		||||
    exec_state: &mut ExecState,
 | 
			
		||||
) -> Result<kittycad::types::Transform, KclError> {
 | 
			
		||||
    // Call the transform fn for this repetition.
 | 
			
		||||
    let repetition_num = KclValue::UserVal(UserVal {
 | 
			
		||||
@ -208,7 +210,7 @@ async fn make_transform<'a>(
 | 
			
		||||
        meta: vec![source_range.into()],
 | 
			
		||||
    });
 | 
			
		||||
    let transform_fn_args = vec![repetition_num];
 | 
			
		||||
    let transform_fn_return = transform_function.call(transform_fn_args).await?;
 | 
			
		||||
    let transform_fn_return = transform_function.call(exec_state, transform_fn_args).await?;
 | 
			
		||||
 | 
			
		||||
    // Unpack the returned transform object.
 | 
			
		||||
    let source_ranges = vec![source_range];
 | 
			
		||||
@ -299,7 +301,7 @@ mod tests {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// A linear pattern on a 2D sketch.
 | 
			
		||||
pub async fn pattern_linear_2d(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn pattern_linear_2d(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (data, sketch_group_set): (LinearPattern2dData, SketchGroupSet) = args.get_data_and_sketch_group_set()?;
 | 
			
		||||
 | 
			
		||||
    if data.axis == [0.0, 0.0] {
 | 
			
		||||
@ -366,7 +368,7 @@ async fn inner_pattern_linear_2d(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// A linear pattern on a 3D model.
 | 
			
		||||
pub async fn pattern_linear_3d(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn pattern_linear_3d(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (data, extrude_group_set): (LinearPattern3dData, ExtrudeGroupSet) = args.get_data_and_extrude_group_set()?;
 | 
			
		||||
 | 
			
		||||
    if data.axis == [0.0, 0.0, 0.0] {
 | 
			
		||||
@ -378,7 +380,7 @@ pub async fn pattern_linear_3d(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
        }));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let extrude_groups = inner_pattern_linear_3d(data, extrude_group_set, args).await?;
 | 
			
		||||
    let extrude_groups = inner_pattern_linear_3d(data, extrude_group_set, exec_state, args).await?;
 | 
			
		||||
    Ok(extrude_groups.into())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -406,12 +408,13 @@ pub async fn pattern_linear_3d(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
async fn inner_pattern_linear_3d(
 | 
			
		||||
    data: LinearPattern3dData,
 | 
			
		||||
    extrude_group_set: ExtrudeGroupSet,
 | 
			
		||||
    exec_state: &mut ExecState,
 | 
			
		||||
    args: Args,
 | 
			
		||||
) -> Result<Vec<Box<ExtrudeGroup>>, KclError> {
 | 
			
		||||
    // Flush the batch for our fillets/chamfers if there are any.
 | 
			
		||||
    // If we do not flush these, then you won't be able to pattern something with fillets.
 | 
			
		||||
    // Flush just the fillets/chamfers that apply to these extrude groups.
 | 
			
		||||
    args.flush_batch_for_extrude_group_set(extrude_group_set.clone().into())
 | 
			
		||||
    args.flush_batch_for_extrude_group_set(exec_state, extrude_group_set.clone().into())
 | 
			
		||||
        .await?;
 | 
			
		||||
 | 
			
		||||
    let starting_extrude_groups: Vec<Box<ExtrudeGroup>> = extrude_group_set.into();
 | 
			
		||||
@ -574,7 +577,7 @@ impl CircularPattern {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// A circular pattern on a 2D sketch.
 | 
			
		||||
pub async fn pattern_circular_2d(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn pattern_circular_2d(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (data, sketch_group_set): (CircularPattern2dData, SketchGroupSet) = args.get_data_and_sketch_group_set()?;
 | 
			
		||||
 | 
			
		||||
    let sketch_groups = inner_pattern_circular_2d(data, sketch_group_set, args).await?;
 | 
			
		||||
@ -639,10 +642,10 @@ async fn inner_pattern_circular_2d(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// A circular pattern on a 3D model.
 | 
			
		||||
pub async fn pattern_circular_3d(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn pattern_circular_3d(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (data, extrude_group_set): (CircularPattern3dData, ExtrudeGroupSet) = args.get_data_and_extrude_group_set()?;
 | 
			
		||||
 | 
			
		||||
    let extrude_groups = inner_pattern_circular_3d(data, extrude_group_set, args).await?;
 | 
			
		||||
    let extrude_groups = inner_pattern_circular_3d(data, extrude_group_set, exec_state, args).await?;
 | 
			
		||||
    Ok(extrude_groups.into())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -670,12 +673,13 @@ pub async fn pattern_circular_3d(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
async fn inner_pattern_circular_3d(
 | 
			
		||||
    data: CircularPattern3dData,
 | 
			
		||||
    extrude_group_set: ExtrudeGroupSet,
 | 
			
		||||
    exec_state: &mut ExecState,
 | 
			
		||||
    args: Args,
 | 
			
		||||
) -> Result<Vec<Box<ExtrudeGroup>>, KclError> {
 | 
			
		||||
    // Flush the batch for our fillets/chamfers if there are any.
 | 
			
		||||
    // If we do not flush these, then you won't be able to pattern something with fillets.
 | 
			
		||||
    // Flush just the fillets/chamfers that apply to these extrude groups.
 | 
			
		||||
    args.flush_batch_for_extrude_group_set(extrude_group_set.clone().into())
 | 
			
		||||
    args.flush_batch_for_extrude_group_set(exec_state, extrude_group_set.clone().into())
 | 
			
		||||
        .await?;
 | 
			
		||||
 | 
			
		||||
    let starting_extrude_groups: Vec<Box<ExtrudeGroup>> = extrude_group_set.into();
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize};
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::KclError,
 | 
			
		||||
    executor::{KclValue, Metadata, Plane, UserVal},
 | 
			
		||||
    executor::{ExecState, KclValue, Metadata, Plane, UserVal},
 | 
			
		||||
    std::{sketch::PlaneData, Args},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -48,7 +48,7 @@ impl From<StandardPlane> for PlaneData {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Offset a plane by a distance along its normal.
 | 
			
		||||
pub async fn offset_plane(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn offset_plane(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (std_plane, offset): (StandardPlane, f64) = args.get_data_and_float()?;
 | 
			
		||||
 | 
			
		||||
    let plane = inner_offset_plane(std_plane, offset).await?;
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,11 @@ use derive_docs::stdlib;
 | 
			
		||||
use schemars::JsonSchema;
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
 | 
			
		||||
use crate::{errors::KclError, executor::KclValue, std::Args};
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::KclError,
 | 
			
		||||
    executor::{ExecState, KclValue},
 | 
			
		||||
    std::Args,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Data for polar coordinates.
 | 
			
		||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
 | 
			
		||||
@ -19,7 +23,7 @@ pub struct PolarCoordsData {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Convert from polar/sphere coordinates to cartesian coordinates.
 | 
			
		||||
pub async fn polar(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn polar(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let data: PolarCoordsData = args.get_data()?;
 | 
			
		||||
    let result = inner_polar(&data)?;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    executor::{ExtrudeGroup, KclValue, SketchGroup},
 | 
			
		||||
    executor::{ExecState, ExtrudeGroup, KclValue, SketchGroup},
 | 
			
		||||
    std::{
 | 
			
		||||
        extrude::do_post_extrude,
 | 
			
		||||
        fillet::{default_tolerance, EdgeReference},
 | 
			
		||||
@ -101,10 +101,10 @@ impl RevolveAxisAndOrigin {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Revolve a sketch around an axis.
 | 
			
		||||
pub async fn revolve(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn revolve(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (data, sketch_group): (RevolveData, SketchGroup) = args.get_data_and_sketch_group()?;
 | 
			
		||||
 | 
			
		||||
    let extrude_group = inner_revolve(data, sketch_group, args).await?;
 | 
			
		||||
    let extrude_group = inner_revolve(data, sketch_group, exec_state, args).await?;
 | 
			
		||||
    Ok(KclValue::ExtrudeGroup(extrude_group))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -250,6 +250,7 @@ pub async fn revolve(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
async fn inner_revolve(
 | 
			
		||||
    data: RevolveData,
 | 
			
		||||
    sketch_group: SketchGroup,
 | 
			
		||||
    exec_state: &mut ExecState,
 | 
			
		||||
    args: Args,
 | 
			
		||||
) -> Result<Box<ExtrudeGroup>, KclError> {
 | 
			
		||||
    if let Some(angle) = data.angle {
 | 
			
		||||
@ -284,7 +285,7 @@ async fn inner_revolve(
 | 
			
		||||
        RevolveAxis::Edge(edge) => {
 | 
			
		||||
            let edge_id = match edge {
 | 
			
		||||
                EdgeReference::Uuid(uuid) => uuid,
 | 
			
		||||
                EdgeReference::Tag(tag) => args.get_tag_engine_info(&tag)?.id,
 | 
			
		||||
                EdgeReference::Tag(tag) => args.get_tag_engine_info(exec_state, &tag)?.id,
 | 
			
		||||
            };
 | 
			
		||||
            args.batch_modeling_cmd(
 | 
			
		||||
                id,
 | 
			
		||||
 | 
			
		||||
@ -6,14 +6,14 @@ use schemars::JsonSchema;
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    executor::{KclValue, SketchGroup, TagIdentifier},
 | 
			
		||||
    executor::{ExecState, KclValue, SketchGroup, TagIdentifier},
 | 
			
		||||
    std::{utils::between, Args},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Returns the segment end of x.
 | 
			
		||||
pub async fn segment_end_x(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn segment_end_x(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let tag: TagIdentifier = args.get_data()?;
 | 
			
		||||
    let result = inner_segment_end_x(&tag, args.clone())?;
 | 
			
		||||
    let result = inner_segment_end_x(&tag, exec_state, args.clone())?;
 | 
			
		||||
 | 
			
		||||
    args.make_user_val_from_f64(result)
 | 
			
		||||
}
 | 
			
		||||
@ -34,8 +34,8 @@ pub async fn segment_end_x(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
#[stdlib {
 | 
			
		||||
    name = "segEndX",
 | 
			
		||||
}]
 | 
			
		||||
fn inner_segment_end_x(tag: &TagIdentifier, args: Args) -> Result<f64, KclError> {
 | 
			
		||||
    let line = args.get_tag_engine_info(tag)?;
 | 
			
		||||
fn inner_segment_end_x(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<f64, KclError> {
 | 
			
		||||
    let line = args.get_tag_engine_info(exec_state, tag)?;
 | 
			
		||||
    let path = line.path.clone().ok_or_else(|| {
 | 
			
		||||
        KclError::Type(KclErrorDetails {
 | 
			
		||||
            message: format!("Expected a line segment with a path, found `{:?}`", line),
 | 
			
		||||
@ -47,9 +47,9 @@ fn inner_segment_end_x(tag: &TagIdentifier, args: Args) -> Result<f64, KclError>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Returns the segment end of y.
 | 
			
		||||
pub async fn segment_end_y(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn segment_end_y(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let tag: TagIdentifier = args.get_data()?;
 | 
			
		||||
    let result = inner_segment_end_y(&tag, args.clone())?;
 | 
			
		||||
    let result = inner_segment_end_y(&tag, exec_state, args.clone())?;
 | 
			
		||||
 | 
			
		||||
    args.make_user_val_from_f64(result)
 | 
			
		||||
}
 | 
			
		||||
@ -71,8 +71,8 @@ pub async fn segment_end_y(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
#[stdlib {
 | 
			
		||||
    name = "segEndY",
 | 
			
		||||
}]
 | 
			
		||||
fn inner_segment_end_y(tag: &TagIdentifier, args: Args) -> Result<f64, KclError> {
 | 
			
		||||
    let line = args.get_tag_engine_info(tag)?;
 | 
			
		||||
fn inner_segment_end_y(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<f64, KclError> {
 | 
			
		||||
    let line = args.get_tag_engine_info(exec_state, tag)?;
 | 
			
		||||
    let path = line.path.clone().ok_or_else(|| {
 | 
			
		||||
        KclError::Type(KclErrorDetails {
 | 
			
		||||
            message: format!("Expected a line segment with a path, found `{:?}`", line),
 | 
			
		||||
@ -84,7 +84,7 @@ fn inner_segment_end_y(tag: &TagIdentifier, args: Args) -> Result<f64, KclError>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Returns the last segment of x.
 | 
			
		||||
pub async fn last_segment_x(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn last_segment_x(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let sketch_group = args.get_sketch_group()?;
 | 
			
		||||
    let result = inner_last_segment_x(sketch_group, args.clone())?;
 | 
			
		||||
 | 
			
		||||
@ -127,7 +127,7 @@ fn inner_last_segment_x(sketch_group: SketchGroup, args: Args) -> Result<f64, Kc
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Returns the last segment of y.
 | 
			
		||||
pub async fn last_segment_y(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn last_segment_y(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let sketch_group = args.get_sketch_group()?;
 | 
			
		||||
    let result = inner_last_segment_y(sketch_group, args.clone())?;
 | 
			
		||||
 | 
			
		||||
@ -170,9 +170,9 @@ fn inner_last_segment_y(sketch_group: SketchGroup, args: Args) -> Result<f64, Kc
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Returns the length of the segment.
 | 
			
		||||
pub async fn segment_length(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn segment_length(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let tag: TagIdentifier = args.get_data()?;
 | 
			
		||||
    let result = inner_segment_length(&tag, args.clone())?;
 | 
			
		||||
    let result = inner_segment_length(&tag, exec_state, args.clone())?;
 | 
			
		||||
    args.make_user_val_from_f64(result)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -200,8 +200,8 @@ pub async fn segment_length(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
#[stdlib {
 | 
			
		||||
    name = "segLen",
 | 
			
		||||
}]
 | 
			
		||||
fn inner_segment_length(tag: &TagIdentifier, args: Args) -> Result<f64, KclError> {
 | 
			
		||||
    let line = args.get_tag_engine_info(tag)?;
 | 
			
		||||
fn inner_segment_length(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<f64, KclError> {
 | 
			
		||||
    let line = args.get_tag_engine_info(exec_state, tag)?;
 | 
			
		||||
    let path = line.path.clone().ok_or_else(|| {
 | 
			
		||||
        KclError::Type(KclErrorDetails {
 | 
			
		||||
            message: format!("Expected a line segment with a path, found `{:?}`", line),
 | 
			
		||||
@ -215,10 +215,10 @@ fn inner_segment_length(tag: &TagIdentifier, args: Args) -> Result<f64, KclError
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Returns the angle of the segment.
 | 
			
		||||
pub async fn segment_angle(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn segment_angle(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let tag: TagIdentifier = args.get_data()?;
 | 
			
		||||
 | 
			
		||||
    let result = inner_segment_angle(&tag, args.clone())?;
 | 
			
		||||
    let result = inner_segment_angle(&tag, exec_state, args.clone())?;
 | 
			
		||||
    args.make_user_val_from_f64(result)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -240,8 +240,8 @@ pub async fn segment_angle(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
#[stdlib {
 | 
			
		||||
    name = "segAng",
 | 
			
		||||
}]
 | 
			
		||||
fn inner_segment_angle(tag: &TagIdentifier, args: Args) -> Result<f64, KclError> {
 | 
			
		||||
    let line = args.get_tag_engine_info(tag)?;
 | 
			
		||||
fn inner_segment_angle(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<f64, KclError> {
 | 
			
		||||
    let line = args.get_tag_engine_info(exec_state, tag)?;
 | 
			
		||||
    let path = line.path.clone().ok_or_else(|| {
 | 
			
		||||
        KclError::Type(KclErrorDetails {
 | 
			
		||||
            message: format!("Expected a line segment with a path, found `{:?}`", line),
 | 
			
		||||
@ -255,9 +255,9 @@ fn inner_segment_angle(tag: &TagIdentifier, args: Args) -> Result<f64, KclError>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Returns the angle to match the given length for x.
 | 
			
		||||
pub async fn angle_to_match_length_x(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn angle_to_match_length_x(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (tag, to, sketch_group) = args.get_tag_to_number_sketch_group()?;
 | 
			
		||||
    let result = inner_angle_to_match_length_x(&tag, to, sketch_group, args.clone())?;
 | 
			
		||||
    let result = inner_angle_to_match_length_x(&tag, to, sketch_group, exec_state, args.clone())?;
 | 
			
		||||
    args.make_user_val_from_f64(result)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -282,9 +282,10 @@ fn inner_angle_to_match_length_x(
 | 
			
		||||
    tag: &TagIdentifier,
 | 
			
		||||
    to: f64,
 | 
			
		||||
    sketch_group: SketchGroup,
 | 
			
		||||
    exec_state: &mut ExecState,
 | 
			
		||||
    args: Args,
 | 
			
		||||
) -> Result<f64, KclError> {
 | 
			
		||||
    let line = args.get_tag_engine_info(tag)?;
 | 
			
		||||
    let line = args.get_tag_engine_info(exec_state, tag)?;
 | 
			
		||||
    let path = line.path.clone().ok_or_else(|| {
 | 
			
		||||
        KclError::Type(KclErrorDetails {
 | 
			
		||||
            message: format!("Expected a line segment with a path, found `{:?}`", line),
 | 
			
		||||
@ -320,9 +321,9 @@ fn inner_angle_to_match_length_x(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Returns the angle to match the given length for y.
 | 
			
		||||
pub async fn angle_to_match_length_y(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn angle_to_match_length_y(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (tag, to, sketch_group) = args.get_tag_to_number_sketch_group()?;
 | 
			
		||||
    let result = inner_angle_to_match_length_y(&tag, to, sketch_group, args.clone())?;
 | 
			
		||||
    let result = inner_angle_to_match_length_y(&tag, to, sketch_group, exec_state, args.clone())?;
 | 
			
		||||
    args.make_user_val_from_f64(result)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -348,9 +349,10 @@ fn inner_angle_to_match_length_y(
 | 
			
		||||
    tag: &TagIdentifier,
 | 
			
		||||
    to: f64,
 | 
			
		||||
    sketch_group: SketchGroup,
 | 
			
		||||
    exec_state: &mut ExecState,
 | 
			
		||||
    args: Args,
 | 
			
		||||
) -> Result<f64, KclError> {
 | 
			
		||||
    let line = args.get_tag_engine_info(tag)?;
 | 
			
		||||
    let line = args.get_tag_engine_info(exec_state, tag)?;
 | 
			
		||||
    let path = line.path.clone().ok_or_else(|| {
 | 
			
		||||
        KclError::Type(KclErrorDetails {
 | 
			
		||||
            message: format!("Expected a line segment with a path, found `{:?}`", line),
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
 | 
			
		||||
use crate::{
 | 
			
		||||
    ast::types::TagDeclarator,
 | 
			
		||||
    errors::KclError,
 | 
			
		||||
    executor::KclValue,
 | 
			
		||||
    executor::{ExecState, KclValue},
 | 
			
		||||
    std::{Args, SketchGroup, SketchSurface},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -22,11 +22,11 @@ pub enum SketchSurfaceOrGroup {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Sketch a circle.
 | 
			
		||||
pub async fn circle(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn circle(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (center, radius, sketch_surface_or_group, tag): ([f64; 2], f64, SketchSurfaceOrGroup, Option<TagDeclarator>) =
 | 
			
		||||
        args.get_circle_args()?;
 | 
			
		||||
 | 
			
		||||
    let sketch_group = inner_circle(center, radius, sketch_surface_or_group, tag, args).await?;
 | 
			
		||||
    let sketch_group = inner_circle(center, radius, sketch_surface_or_group, tag, exec_state, args).await?;
 | 
			
		||||
    Ok(KclValue::new_user_val(sketch_group.meta.clone(), sketch_group))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -59,15 +59,21 @@ async fn inner_circle(
 | 
			
		||||
    radius: f64,
 | 
			
		||||
    sketch_surface_or_group: SketchSurfaceOrGroup,
 | 
			
		||||
    tag: Option<TagDeclarator>,
 | 
			
		||||
    exec_state: &mut ExecState,
 | 
			
		||||
    args: Args,
 | 
			
		||||
) -> Result<SketchGroup, KclError> {
 | 
			
		||||
    let sketch_surface = match sketch_surface_or_group {
 | 
			
		||||
        SketchSurfaceOrGroup::SketchSurface(surface) => surface,
 | 
			
		||||
        SketchSurfaceOrGroup::SketchGroup(group) => group.on,
 | 
			
		||||
    };
 | 
			
		||||
    let mut sketch_group =
 | 
			
		||||
        crate::std::sketch::inner_start_profile_at([center[0] + radius, center[1]], sketch_surface, None, args.clone())
 | 
			
		||||
            .await?;
 | 
			
		||||
    let mut sketch_group = crate::std::sketch::inner_start_profile_at(
 | 
			
		||||
        [center[0] + radius, center[1]],
 | 
			
		||||
        sketch_surface,
 | 
			
		||||
        None,
 | 
			
		||||
        exec_state,
 | 
			
		||||
        args.clone(),
 | 
			
		||||
    )
 | 
			
		||||
    .await?;
 | 
			
		||||
 | 
			
		||||
    // Call arc.
 | 
			
		||||
    sketch_group = crate::std::sketch::inner_arc(
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    executor::{ExtrudeGroup, KclValue},
 | 
			
		||||
    executor::{ExecState, ExtrudeGroup, KclValue},
 | 
			
		||||
    std::{sketch::FaceTag, Args},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -24,10 +24,10 @@ pub struct ShellData {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Create a shell.
 | 
			
		||||
pub async fn shell(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn shell(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (data, extrude_group): (ShellData, Box<ExtrudeGroup>) = args.get_data_and_extrude_group()?;
 | 
			
		||||
 | 
			
		||||
    let extrude_group = inner_shell(data, extrude_group, args).await?;
 | 
			
		||||
    let extrude_group = inner_shell(data, extrude_group, exec_state, args).await?;
 | 
			
		||||
    Ok(KclValue::ExtrudeGroup(extrude_group))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -154,6 +154,7 @@ pub async fn shell(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
async fn inner_shell(
 | 
			
		||||
    data: ShellData,
 | 
			
		||||
    extrude_group: Box<ExtrudeGroup>,
 | 
			
		||||
    exec_state: &mut ExecState,
 | 
			
		||||
    args: Args,
 | 
			
		||||
) -> Result<Box<ExtrudeGroup>, KclError> {
 | 
			
		||||
    if data.faces.is_empty() {
 | 
			
		||||
@ -165,7 +166,7 @@ async fn inner_shell(
 | 
			
		||||
 | 
			
		||||
    let mut face_ids = Vec::new();
 | 
			
		||||
    for tag in data.faces {
 | 
			
		||||
        let extrude_plane_id = tag.get_face_id(&extrude_group, &args, false).await?;
 | 
			
		||||
        let extrude_plane_id = tag.get_face_id(&extrude_group, exec_state, &args, false).await?;
 | 
			
		||||
 | 
			
		||||
        face_ids.push(extrude_plane_id);
 | 
			
		||||
    }
 | 
			
		||||
@ -179,7 +180,7 @@ async fn inner_shell(
 | 
			
		||||
 | 
			
		||||
    // Flush the batch for our fillets/chamfers if there are any.
 | 
			
		||||
    // If we do not do these for sketch on face, things will fail with face does not exist.
 | 
			
		||||
    args.flush_batch_for_extrude_group_set(extrude_group.clone().into())
 | 
			
		||||
    args.flush_batch_for_extrude_group_set(exec_state, extrude_group.clone().into())
 | 
			
		||||
        .await?;
 | 
			
		||||
 | 
			
		||||
    args.batch_modeling_cmd(
 | 
			
		||||
@ -197,10 +198,10 @@ async fn inner_shell(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Make the inside of a 3D object hollow.
 | 
			
		||||
pub async fn hollow(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn hollow(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (thickness, extrude_group): (f64, Box<ExtrudeGroup>) = args.get_data_and_extrude_group()?;
 | 
			
		||||
 | 
			
		||||
    let extrude_group = inner_hollow(thickness, extrude_group, args).await?;
 | 
			
		||||
    let extrude_group = inner_hollow(thickness, extrude_group, exec_state, args).await?;
 | 
			
		||||
    Ok(KclValue::ExtrudeGroup(extrude_group))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -236,11 +237,12 @@ pub async fn hollow(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
async fn inner_hollow(
 | 
			
		||||
    thickness: f64,
 | 
			
		||||
    extrude_group: Box<ExtrudeGroup>,
 | 
			
		||||
    exec_state: &mut ExecState,
 | 
			
		||||
    args: Args,
 | 
			
		||||
) -> Result<Box<ExtrudeGroup>, KclError> {
 | 
			
		||||
    // Flush the batch for our fillets/chamfers if there are any.
 | 
			
		||||
    // If we do not do these for sketch on face, things will fail with face does not exist.
 | 
			
		||||
    args.flush_batch_for_extrude_group_set(extrude_group.clone().into())
 | 
			
		||||
    args.flush_batch_for_extrude_group_set(exec_state, extrude_group.clone().into())
 | 
			
		||||
        .await?;
 | 
			
		||||
 | 
			
		||||
    args.batch_modeling_cmd(
 | 
			
		||||
 | 
			
		||||
@ -13,8 +13,8 @@ use crate::{
 | 
			
		||||
    ast::types::TagDeclarator,
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    executor::{
 | 
			
		||||
        BasePath, ExtrudeGroup, Face, GeoMeta, KclValue, Path, Plane, PlaneType, Point2d, Point3d, SketchGroup,
 | 
			
		||||
        SketchGroupSet, SketchSurface, TagEngineInfo, TagIdentifier, UserVal,
 | 
			
		||||
        BasePath, ExecState, ExtrudeGroup, Face, GeoMeta, KclValue, Path, Plane, PlaneType, Point2d, Point3d,
 | 
			
		||||
        SketchGroup, SketchGroupSet, SketchSurface, TagEngineInfo, TagIdentifier, UserVal,
 | 
			
		||||
    },
 | 
			
		||||
    std::{
 | 
			
		||||
        utils::{
 | 
			
		||||
@ -50,11 +50,12 @@ impl FaceTag {
 | 
			
		||||
    pub async fn get_face_id(
 | 
			
		||||
        &self,
 | 
			
		||||
        extrude_group: &ExtrudeGroup,
 | 
			
		||||
        exec_state: &mut ExecState,
 | 
			
		||||
        args: &Args,
 | 
			
		||||
        must_be_planar: bool,
 | 
			
		||||
    ) -> Result<uuid::Uuid, KclError> {
 | 
			
		||||
        match self {
 | 
			
		||||
            FaceTag::Tag(ref t) => args.get_adjacent_face_to_tag(t, must_be_planar).await,
 | 
			
		||||
            FaceTag::Tag(ref t) => args.get_adjacent_face_to_tag(exec_state, t, must_be_planar).await,
 | 
			
		||||
            FaceTag::StartOrEnd(StartOrEnd::Start) => extrude_group.start_cap_id.ok_or_else(|| {
 | 
			
		||||
                KclError::Type(KclErrorDetails {
 | 
			
		||||
                    message: "Expected a start face".to_string(),
 | 
			
		||||
@ -89,7 +90,7 @@ pub enum StartOrEnd {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Draw a line to a point.
 | 
			
		||||
pub async fn line_to(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn line_to(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (to, sketch_group, tag): ([f64; 2], SketchGroup, Option<TagDeclarator>) =
 | 
			
		||||
        args.get_data_and_sketch_group_and_tag()?;
 | 
			
		||||
 | 
			
		||||
@ -160,7 +161,7 @@ async fn inner_line_to(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Draw a line to a point on the x-axis.
 | 
			
		||||
pub async fn x_line_to(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn x_line_to(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (to, sketch_group, tag): (f64, SketchGroup, Option<TagDeclarator>) =
 | 
			
		||||
        args.get_data_and_sketch_group_and_tag()?;
 | 
			
		||||
 | 
			
		||||
@ -208,7 +209,7 @@ async fn inner_x_line_to(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Draw a line to a point on the y-axis.
 | 
			
		||||
pub async fn y_line_to(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn y_line_to(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (to, sketch_group, tag): (f64, SketchGroup, Option<TagDeclarator>) =
 | 
			
		||||
        args.get_data_and_sketch_group_and_tag()?;
 | 
			
		||||
 | 
			
		||||
@ -248,7 +249,7 @@ async fn inner_y_line_to(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Draw a line.
 | 
			
		||||
pub async fn line(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn line(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (delta, sketch_group, tag): ([f64; 2], SketchGroup, Option<TagDeclarator>) =
 | 
			
		||||
        args.get_data_and_sketch_group_and_tag()?;
 | 
			
		||||
 | 
			
		||||
@ -333,7 +334,7 @@ async fn inner_line(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Draw a line on the x-axis.
 | 
			
		||||
pub async fn x_line(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn x_line(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (length, sketch_group, tag): (f64, SketchGroup, Option<TagDeclarator>) =
 | 
			
		||||
        args.get_data_and_sketch_group_and_tag()?;
 | 
			
		||||
 | 
			
		||||
@ -376,7 +377,7 @@ async fn inner_x_line(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Draw a line on the y-axis.
 | 
			
		||||
pub async fn y_line(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn y_line(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (length, sketch_group, tag): (f64, SketchGroup, Option<TagDeclarator>) =
 | 
			
		||||
        args.get_data_and_sketch_group_and_tag()?;
 | 
			
		||||
 | 
			
		||||
@ -430,7 +431,7 @@ pub enum AngledLineData {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Draw an angled line.
 | 
			
		||||
pub async fn angled_line(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn angled_line(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (data, sketch_group, tag): (AngledLineData, SketchGroup, Option<TagDeclarator>) =
 | 
			
		||||
        args.get_data_and_sketch_group_and_tag()?;
 | 
			
		||||
 | 
			
		||||
@ -519,7 +520,7 @@ async fn inner_angled_line(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Draw an angled line of a given x length.
 | 
			
		||||
pub async fn angled_line_of_x_length(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn angled_line_of_x_length(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (data, sketch_group, tag): (AngledLineData, SketchGroup, Option<TagDeclarator>) =
 | 
			
		||||
        args.get_data_and_sketch_group_and_tag()?;
 | 
			
		||||
 | 
			
		||||
@ -587,7 +588,7 @@ pub struct AngledLineToData {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Draw an angled line to a given x coordinate.
 | 
			
		||||
pub async fn angled_line_to_x(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn angled_line_to_x(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (data, sketch_group, tag): (AngledLineToData, SketchGroup, Option<TagDeclarator>) =
 | 
			
		||||
        args.get_data_and_sketch_group_and_tag()?;
 | 
			
		||||
 | 
			
		||||
@ -644,7 +645,7 @@ async fn inner_angled_line_to_x(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Draw an angled line of a given y length.
 | 
			
		||||
pub async fn angled_line_of_y_length(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn angled_line_of_y_length(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (data, sketch_group, tag): (AngledLineData, SketchGroup, Option<TagDeclarator>) =
 | 
			
		||||
        args.get_data_and_sketch_group_and_tag()?;
 | 
			
		||||
 | 
			
		||||
@ -704,7 +705,7 @@ async fn inner_angled_line_of_y_length(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Draw an angled line to a given y coordinate.
 | 
			
		||||
pub async fn angled_line_to_y(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn angled_line_to_y(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (data, sketch_group, tag): (AngledLineToData, SketchGroup, Option<TagDeclarator>) =
 | 
			
		||||
        args.get_data_and_sketch_group_and_tag()?;
 | 
			
		||||
 | 
			
		||||
@ -775,10 +776,10 @@ pub struct AngledLineThatIntersectsData {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Draw an angled line that intersects with a given line.
 | 
			
		||||
pub async fn angled_line_that_intersects(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn angled_line_that_intersects(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (data, sketch_group, tag): (AngledLineThatIntersectsData, SketchGroup, Option<TagDeclarator>) =
 | 
			
		||||
        args.get_data_and_sketch_group_and_tag()?;
 | 
			
		||||
    let new_sketch_group = inner_angled_line_that_intersects(data, sketch_group, tag, args).await?;
 | 
			
		||||
    let new_sketch_group = inner_angled_line_that_intersects(data, sketch_group, tag, exec_state, args).await?;
 | 
			
		||||
    Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -808,9 +809,10 @@ async fn inner_angled_line_that_intersects(
 | 
			
		||||
    data: AngledLineThatIntersectsData,
 | 
			
		||||
    sketch_group: SketchGroup,
 | 
			
		||||
    tag: Option<TagDeclarator>,
 | 
			
		||||
    exec_state: &mut ExecState,
 | 
			
		||||
    args: Args,
 | 
			
		||||
) -> Result<SketchGroup, KclError> {
 | 
			
		||||
    let intersect_path = args.get_tag_engine_info(&data.intersect_tag)?;
 | 
			
		||||
    let intersect_path = args.get_tag_engine_info(exec_state, &data.intersect_tag)?;
 | 
			
		||||
    let path = intersect_path.path.clone().ok_or_else(|| {
 | 
			
		||||
        KclError::Type(KclErrorDetails {
 | 
			
		||||
            message: format!("Expected an intersect path with a path, found `{:?}`", intersect_path),
 | 
			
		||||
@ -831,10 +833,10 @@ async fn inner_angled_line_that_intersects(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Start a sketch at a given point.
 | 
			
		||||
pub async fn start_sketch_at(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn start_sketch_at(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let data: [f64; 2] = args.get_data()?;
 | 
			
		||||
 | 
			
		||||
    let sketch_group = inner_start_sketch_at(data, args).await?;
 | 
			
		||||
    let sketch_group = inner_start_sketch_at(data, exec_state, args).await?;
 | 
			
		||||
    Ok(KclValue::new_user_val(sketch_group.meta.clone(), sketch_group))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -872,11 +874,15 @@ pub async fn start_sketch_at(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
#[stdlib {
 | 
			
		||||
    name = "startSketchAt",
 | 
			
		||||
}]
 | 
			
		||||
async fn inner_start_sketch_at(data: [f64; 2], args: Args) -> Result<SketchGroup, KclError> {
 | 
			
		||||
async fn inner_start_sketch_at(
 | 
			
		||||
    data: [f64; 2],
 | 
			
		||||
    exec_state: &mut ExecState,
 | 
			
		||||
    args: Args,
 | 
			
		||||
) -> Result<SketchGroup, KclError> {
 | 
			
		||||
    // Let's assume it's the XY plane for now, this is just for backwards compatibility.
 | 
			
		||||
    let xy_plane = PlaneData::XY;
 | 
			
		||||
    let sketch_surface = inner_start_sketch_on(SketchData::Plane(xy_plane), None, args.clone()).await?;
 | 
			
		||||
    let sketch_group = inner_start_profile_at(data, sketch_surface, None, args).await?;
 | 
			
		||||
    let sketch_surface = inner_start_sketch_on(SketchData::Plane(xy_plane), None, exec_state, &args).await?;
 | 
			
		||||
    let sketch_group = inner_start_profile_at(data, sketch_surface, None, exec_state, args).await?;
 | 
			
		||||
    Ok(sketch_group)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1006,10 +1012,10 @@ impl From<PlaneData> for Plane {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Start a sketch on a specific plane or face.
 | 
			
		||||
pub async fn start_sketch_on(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn start_sketch_on(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (data, tag): (SketchData, Option<FaceTag>) = args.get_data_and_optional_tag()?;
 | 
			
		||||
 | 
			
		||||
    match inner_start_sketch_on(data, tag, args).await? {
 | 
			
		||||
    match inner_start_sketch_on(data, tag, exec_state, &args).await? {
 | 
			
		||||
        SketchSurface::Plane(plane) => Ok(KclValue::Plane(plane)),
 | 
			
		||||
        SketchSurface::Face(face) => Ok(KclValue::Face(face)),
 | 
			
		||||
    }
 | 
			
		||||
@ -1119,7 +1125,12 @@ pub async fn start_sketch_on(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
#[stdlib {
 | 
			
		||||
    name = "startSketchOn",
 | 
			
		||||
}]
 | 
			
		||||
async fn inner_start_sketch_on(data: SketchData, tag: Option<FaceTag>, args: Args) -> Result<SketchSurface, KclError> {
 | 
			
		||||
async fn inner_start_sketch_on(
 | 
			
		||||
    data: SketchData,
 | 
			
		||||
    tag: Option<FaceTag>,
 | 
			
		||||
    exec_state: &mut ExecState,
 | 
			
		||||
    args: &Args,
 | 
			
		||||
) -> Result<SketchSurface, KclError> {
 | 
			
		||||
    match data {
 | 
			
		||||
        SketchData::Plane(plane_data) => {
 | 
			
		||||
            let plane = start_sketch_on_plane(plane_data, args).await?;
 | 
			
		||||
@ -1132,7 +1143,7 @@ async fn inner_start_sketch_on(data: SketchData, tag: Option<FaceTag>, args: Arg
 | 
			
		||||
                    source_ranges: vec![args.source_range],
 | 
			
		||||
                }));
 | 
			
		||||
            };
 | 
			
		||||
            let face = start_sketch_on_face(extrude_group, tag, args).await?;
 | 
			
		||||
            let face = start_sketch_on_face(extrude_group, tag, exec_state, args).await?;
 | 
			
		||||
            Ok(SketchSurface::Face(face))
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@ -1141,9 +1152,10 @@ async fn inner_start_sketch_on(data: SketchData, tag: Option<FaceTag>, args: Arg
 | 
			
		||||
async fn start_sketch_on_face(
 | 
			
		||||
    extrude_group: Box<ExtrudeGroup>,
 | 
			
		||||
    tag: FaceTag,
 | 
			
		||||
    args: Args,
 | 
			
		||||
    exec_state: &mut ExecState,
 | 
			
		||||
    args: &Args,
 | 
			
		||||
) -> Result<Box<Face>, KclError> {
 | 
			
		||||
    let extrude_plane_id = tag.get_face_id(&extrude_group, &args, true).await?;
 | 
			
		||||
    let extrude_plane_id = tag.get_face_id(&extrude_group, exec_state, args, true).await?;
 | 
			
		||||
 | 
			
		||||
    Ok(Box::new(Face {
 | 
			
		||||
        id: extrude_plane_id,
 | 
			
		||||
@ -1157,7 +1169,7 @@ async fn start_sketch_on_face(
 | 
			
		||||
    }))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async fn start_sketch_on_plane(data: PlaneData, args: Args) -> Result<Box<Plane>, KclError> {
 | 
			
		||||
async fn start_sketch_on_plane(data: PlaneData, args: &Args) -> Result<Box<Plane>, KclError> {
 | 
			
		||||
    let mut plane: Plane = data.clone().into();
 | 
			
		||||
 | 
			
		||||
    // Get the default planes.
 | 
			
		||||
@ -1199,11 +1211,11 @@ async fn start_sketch_on_plane(data: PlaneData, args: Args) -> Result<Box<Plane>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Start a new profile at a given point.
 | 
			
		||||
pub async fn start_profile_at(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn start_profile_at(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (start, sketch_surface, tag): ([f64; 2], SketchSurface, Option<TagDeclarator>) =
 | 
			
		||||
        args.get_data_and_sketch_surface()?;
 | 
			
		||||
 | 
			
		||||
    let sketch_group = inner_start_profile_at(start, sketch_surface, tag, args).await?;
 | 
			
		||||
    let sketch_group = inner_start_profile_at(start, sketch_surface, tag, exec_state, args).await?;
 | 
			
		||||
    Ok(KclValue::new_user_val(sketch_group.meta.clone(), sketch_group))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1248,12 +1260,13 @@ pub(crate) async fn inner_start_profile_at(
 | 
			
		||||
    to: [f64; 2],
 | 
			
		||||
    sketch_surface: SketchSurface,
 | 
			
		||||
    tag: Option<TagDeclarator>,
 | 
			
		||||
    exec_state: &mut ExecState,
 | 
			
		||||
    args: Args,
 | 
			
		||||
) -> Result<SketchGroup, KclError> {
 | 
			
		||||
    if let SketchSurface::Face(face) = &sketch_surface {
 | 
			
		||||
        // Flush the batch for our fillets/chamfers if there are any.
 | 
			
		||||
        // If we do not do these for sketch on face, things will fail with face does not exist.
 | 
			
		||||
        args.flush_batch_for_extrude_group_set(face.extrude_group.clone().into())
 | 
			
		||||
        args.flush_batch_for_extrude_group_set(exec_state, face.extrude_group.clone().into())
 | 
			
		||||
            .await?;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -1328,7 +1341,7 @@ pub(crate) async fn inner_start_profile_at(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Returns the X component of the sketch profile start point.
 | 
			
		||||
pub async fn profile_start_x(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn profile_start_x(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let sketch_group: SketchGroup = args.get_sketch_group()?;
 | 
			
		||||
    let x = inner_profile_start_x(sketch_group)?;
 | 
			
		||||
    args.make_user_val_from_f64(x)
 | 
			
		||||
@ -1352,7 +1365,7 @@ pub(crate) fn inner_profile_start_x(sketch_group: SketchGroup) -> Result<f64, Kc
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Returns the Y component of the sketch profile start point.
 | 
			
		||||
pub async fn profile_start_y(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn profile_start_y(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let sketch_group: SketchGroup = args.get_sketch_group()?;
 | 
			
		||||
    let x = inner_profile_start_y(sketch_group)?;
 | 
			
		||||
    args.make_user_val_from_f64(x)
 | 
			
		||||
@ -1375,7 +1388,7 @@ pub(crate) fn inner_profile_start_y(sketch_group: SketchGroup) -> Result<f64, Kc
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Returns the sketch profile start point.
 | 
			
		||||
pub async fn profile_start(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn profile_start(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let sketch_group: SketchGroup = args.get_sketch_group()?;
 | 
			
		||||
    let point = inner_profile_start(sketch_group)?;
 | 
			
		||||
    Ok(KclValue::UserVal(UserVal {
 | 
			
		||||
@ -1409,7 +1422,7 @@ pub(crate) fn inner_profile_start(sketch_group: SketchGroup) -> Result<[f64; 2],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Close the current sketch.
 | 
			
		||||
pub async fn close(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn close(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (sketch_group, tag): (SketchGroup, Option<TagDeclarator>) = args.get_sketch_group_and_optional_tag()?;
 | 
			
		||||
 | 
			
		||||
    let new_sketch_group = inner_close(sketch_group, tag, args).await?;
 | 
			
		||||
@ -1516,7 +1529,7 @@ pub enum ArcData {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Draw an arc.
 | 
			
		||||
pub async fn arc(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn arc(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (data, sketch_group, tag): (ArcData, SketchGroup, Option<TagDeclarator>) =
 | 
			
		||||
        args.get_data_and_sketch_group_and_tag()?;
 | 
			
		||||
 | 
			
		||||
@ -1637,7 +1650,7 @@ pub enum TangentialArcData {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Draw a tangential arc.
 | 
			
		||||
pub async fn tangential_arc(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn tangential_arc(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (data, sketch_group, tag): (TangentialArcData, SketchGroup, Option<TagDeclarator>) =
 | 
			
		||||
        args.get_data_and_sketch_group_and_tag()?;
 | 
			
		||||
 | 
			
		||||
@ -1767,7 +1780,7 @@ fn tan_arc_to(sketch_group: &SketchGroup, to: &[f64; 2]) -> ModelingCmd {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Draw a tangential arc to a specific point.
 | 
			
		||||
pub async fn tangential_arc_to(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn tangential_arc_to(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (to, sketch_group, tag): ([f64; 2], SketchGroup, Option<TagDeclarator>) =
 | 
			
		||||
        super::args::FromArgs::from_args(&args, 0)?;
 | 
			
		||||
 | 
			
		||||
@ -1776,7 +1789,7 @@ pub async fn tangential_arc_to(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Draw a tangential arc to point some distance away..
 | 
			
		||||
pub async fn tangential_arc_to_relative(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn tangential_arc_to_relative(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (delta, sketch_group, tag): ([f64; 2], SketchGroup, Option<TagDeclarator>) =
 | 
			
		||||
        super::args::FromArgs::from_args(&args, 0)?;
 | 
			
		||||
 | 
			
		||||
@ -1951,7 +1964,7 @@ pub struct BezierData {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Draw a bezier curve.
 | 
			
		||||
pub async fn bezier_curve(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn bezier_curve(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (data, sketch_group, tag): (BezierData, SketchGroup, Option<TagDeclarator>) =
 | 
			
		||||
        args.get_data_and_sketch_group_and_tag()?;
 | 
			
		||||
 | 
			
		||||
@ -2043,7 +2056,7 @@ async fn inner_bezier_curve(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Use a sketch to cut a hole in another sketch.
 | 
			
		||||
pub async fn hole(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn hole(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (hole_sketch_group, sketch_group): (SketchGroupSet, SketchGroup) = args.get_sketch_groups()?;
 | 
			
		||||
 | 
			
		||||
    let new_sketch_group = inner_hole(hole_sketch_group, sketch_group, args).await?;
 | 
			
		||||
 | 
			
		||||
@ -4,10 +4,15 @@ use anyhow::Result;
 | 
			
		||||
use derive_docs::stdlib;
 | 
			
		||||
use schemars::JsonSchema;
 | 
			
		||||
 | 
			
		||||
use crate::{errors::KclError, executor::KclValue, settings::types::UnitLength, std::Args};
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::KclError,
 | 
			
		||||
    executor::{ExecState, KclValue},
 | 
			
		||||
    settings::types::UnitLength,
 | 
			
		||||
    std::Args,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Millimeters conversion factor for current projects units.
 | 
			
		||||
pub async fn mm(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn mm(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let result = inner_mm(&args)?;
 | 
			
		||||
 | 
			
		||||
    args.make_user_val_from_f64(result)
 | 
			
		||||
@ -48,7 +53,7 @@ fn inner_mm(args: &Args) -> Result<f64, KclError> {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Inches conversion factor for current projects units.
 | 
			
		||||
pub async fn inch(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn inch(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let result = inner_inch(&args)?;
 | 
			
		||||
 | 
			
		||||
    args.make_user_val_from_f64(result)
 | 
			
		||||
@ -89,7 +94,7 @@ fn inner_inch(args: &Args) -> Result<f64, KclError> {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Feet conversion factor for current projects units.
 | 
			
		||||
pub async fn ft(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn ft(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let result = inner_ft(&args)?;
 | 
			
		||||
 | 
			
		||||
    args.make_user_val_from_f64(result)
 | 
			
		||||
@ -131,7 +136,7 @@ fn inner_ft(args: &Args) -> Result<f64, KclError> {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Meters conversion factor for current projects units.
 | 
			
		||||
pub async fn m(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn m(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let result = inner_m(&args)?;
 | 
			
		||||
 | 
			
		||||
    args.make_user_val_from_f64(result)
 | 
			
		||||
@ -173,7 +178,7 @@ fn inner_m(args: &Args) -> Result<f64, KclError> {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Centimeters conversion factor for current projects units.
 | 
			
		||||
pub async fn cm(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn cm(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let result = inner_cm(&args)?;
 | 
			
		||||
 | 
			
		||||
    args.make_user_val_from_f64(result)
 | 
			
		||||
@ -215,7 +220,7 @@ fn inner_cm(args: &Args) -> Result<f64, KclError> {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Yards conversion factor for current projects units.
 | 
			
		||||
pub async fn yd(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
pub async fn yd(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let result = inner_yd(&args)?;
 | 
			
		||||
 | 
			
		||||
    args.make_user_val_from_f64(result)
 | 
			
		||||
 | 
			
		||||
@ -53,13 +53,13 @@ pub async fn execute_wasm(
 | 
			
		||||
        is_mock,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let memory = ctx.run(&program, Some(memory)).await.map_err(String::from)?;
 | 
			
		||||
    let exec_state = ctx.run(&program, Some(memory)).await.map_err(String::from)?;
 | 
			
		||||
 | 
			
		||||
    // The serde-wasm-bindgen does not work here because of weird HashMap issues so we use the
 | 
			
		||||
    // gloo-serialize crate instead.
 | 
			
		||||
    // DO NOT USE serde_wasm_bindgen::to_value(&memory).map_err(|e| e.to_string())
 | 
			
		||||
    // it will break the frontend.
 | 
			
		||||
    JsValue::from_serde(&memory).map_err(|e| e.to_string())
 | 
			
		||||
    JsValue::from_serde(&exec_state.memory).map_err(|e| e.to_string())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// wasm_bindgen wrapper for execute
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,7 @@
 | 
			
		||||
// Make sure pipe value doesn't leak into the function call.
 | 
			
		||||
fn f = (ignored) => {
 | 
			
		||||
  return %
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const answer = %
 | 
			
		||||
  |> f(%)
 | 
			
		||||
@ -85,5 +85,9 @@ gen_test_fail!(
 | 
			
		||||
    object_prop_not_found,
 | 
			
		||||
    "undefined value: Property 'age' not found in object"
 | 
			
		||||
);
 | 
			
		||||
gen_test_fail!(
 | 
			
		||||
    pipe_substitution_inside_function_called_from_pipeline,
 | 
			
		||||
    "semantic: cannot use % outside a pipe expression"
 | 
			
		||||
);
 | 
			
		||||
gen_test!(sketch_group_in_object);
 | 
			
		||||
gen_test!(add_lots);
 | 
			
		||||
 | 
			
		||||
@ -35,12 +35,12 @@ async fn setup(code: &str, name: &str) -> Result<(ExecutorContext, Program, uuid
 | 
			
		||||
    let parser = kcl_lib::parser::Parser::new(tokens);
 | 
			
		||||
    let program = parser.ast()?;
 | 
			
		||||
    let ctx = kcl_lib::executor::ExecutorContext::new(&client, Default::default()).await?;
 | 
			
		||||
    let memory = ctx.run(&program, None).await?;
 | 
			
		||||
    let exec_state = ctx.run(&program, None).await?;
 | 
			
		||||
 | 
			
		||||
    // We need to get the sketch ID.
 | 
			
		||||
    // Get the sketch group ID from memory.
 | 
			
		||||
    let KclValue::UserVal(user_val) = memory.get(name, SourceRange::default()).unwrap() else {
 | 
			
		||||
        anyhow::bail!("part001 not found in memory: {:?}", memory);
 | 
			
		||||
    let KclValue::UserVal(user_val) = exec_state.memory.get(name, SourceRange::default()).unwrap() else {
 | 
			
		||||
        anyhow::bail!("part001 not found in memory: {:?}", exec_state.memory);
 | 
			
		||||
    };
 | 
			
		||||
    let Some((sketch_group, _meta)) = user_val.get::<SketchGroup>() else {
 | 
			
		||||
        anyhow::bail!("part001 was not a SketchGroup");
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user