diff --git a/src/wasm-lib/derive-docs/src/lib.rs b/src/wasm-lib/derive-docs/src/lib.rs index 36b6659f4..6fcf08268 100644 --- a/src/wasm-lib/derive-docs/src/lib.rs +++ b/src/wasm-lib/derive-docs/src/lib.rs @@ -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> + Send>, + Box> + 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(); } diff --git a/src/wasm-lib/derive-docs/src/tests.rs b/src/wasm-lib/derive-docs/src/tests.rs index 48e57a77d..71cf081e6 100644 --- a/src/wasm-lib/derive-docs/src/tests.rs +++ b/src/wasm-lib/derive-docs/src/tests.rs @@ -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( diff --git a/src/wasm-lib/derive-docs/tests/args_with_lifetime.gen b/src/wasm-lib/derive-docs/tests/args_with_lifetime.gen index 29344c185..a2aa4d1da 100644 --- a/src/wasm-lib/derive-docs/tests/args_with_lifetime.gen +++ b/src/wasm-lib/derive-docs/tests/args_with_lifetime.gen @@ -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, - > + Send, + > + Send + + '_, >, > { - Box::pin(someFn(args)) + Box::pin(someFn(exec_state, args)) } impl crate::docs::StdLibFn for SomeFn { diff --git a/src/wasm-lib/derive-docs/tests/args_with_refs.gen b/src/wasm-lib/derive-docs/tests/args_with_refs.gen index 27dd11d8f..cde1aad82 100644 --- a/src/wasm-lib/derive-docs/tests/args_with_refs.gen +++ b/src/wasm-lib/derive-docs/tests/args_with_refs.gen @@ -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, - > + Send, + > + Send + + '_, >, > { - Box::pin(someFn(args)) + Box::pin(someFn(exec_state, args)) } impl crate::docs::StdLibFn for SomeFn { diff --git a/src/wasm-lib/derive-docs/tests/array.gen b/src/wasm-lib/derive-docs/tests/array.gen index 15c6215b9..733270d5c 100644 --- a/src/wasm-lib/derive-docs/tests/array.gen +++ b/src/wasm-lib/derive-docs/tests/array.gen @@ -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, - > + Send, + > + Send + + '_, >, > { - Box::pin(show(args)) + Box::pin(show(exec_state, args)) } impl crate::docs::StdLibFn for Show { diff --git a/src/wasm-lib/derive-docs/tests/box.gen b/src/wasm-lib/derive-docs/tests/box.gen index 03b7d58ba..b88ebfa24 100644 --- a/src/wasm-lib/derive-docs/tests/box.gen +++ b/src/wasm-lib/derive-docs/tests/box.gen @@ -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, - > + Send, + > + Send + + '_, >, > { - Box::pin(show(args)) + Box::pin(show(exec_state, args)) } impl crate::docs::StdLibFn for Show { diff --git a/src/wasm-lib/derive-docs/tests/doc_comment_with_code.gen b/src/wasm-lib/derive-docs/tests/doc_comment_with_code.gen index 0bca8be47..9f4166161 100644 --- a/src/wasm-lib/derive-docs/tests/doc_comment_with_code.gen +++ b/src/wasm-lib/derive-docs/tests/doc_comment_with_code.gen @@ -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, - > + Send, + > + Send + + '_, >, > { - Box::pin(my_func(args)) + Box::pin(my_func(exec_state, args)) } impl crate::docs::StdLibFn for MyFunc { diff --git a/src/wasm-lib/derive-docs/tests/lineTo.gen b/src/wasm-lib/derive-docs/tests/lineTo.gen index e5a3fb0ce..0cd79c088 100644 --- a/src/wasm-lib/derive-docs/tests/lineTo.gen +++ b/src/wasm-lib/derive-docs/tests/lineTo.gen @@ -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, - > + Send, + > + Send + + '_, >, > { - Box::pin(line_to(args)) + Box::pin(line_to(exec_state, args)) } impl crate::docs::StdLibFn for LineTo { diff --git a/src/wasm-lib/derive-docs/tests/min.gen b/src/wasm-lib/derive-docs/tests/min.gen index 9f2f70682..1b5fbb3bf 100644 --- a/src/wasm-lib/derive-docs/tests/min.gen +++ b/src/wasm-lib/derive-docs/tests/min.gen @@ -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, - > + Send, + > + Send + + '_, >, > { - Box::pin(min(args)) + Box::pin(min(exec_state, args)) } impl crate::docs::StdLibFn for Min { diff --git a/src/wasm-lib/derive-docs/tests/option.gen b/src/wasm-lib/derive-docs/tests/option.gen index cc0d52691..ab593deb6 100644 --- a/src/wasm-lib/derive-docs/tests/option.gen +++ b/src/wasm-lib/derive-docs/tests/option.gen @@ -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, - > + Send, + > + Send + + '_, >, > { - Box::pin(show(args)) + Box::pin(show(exec_state, args)) } impl crate::docs::StdLibFn for Show { diff --git a/src/wasm-lib/derive-docs/tests/option_input_format.gen b/src/wasm-lib/derive-docs/tests/option_input_format.gen index 7fe11a06d..dc90f224e 100644 --- a/src/wasm-lib/derive-docs/tests/option_input_format.gen +++ b/src/wasm-lib/derive-docs/tests/option_input_format.gen @@ -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, - > + Send, + > + Send + + '_, >, > { - Box::pin(import(args)) + Box::pin(import(exec_state, args)) } impl crate::docs::StdLibFn for Import { diff --git a/src/wasm-lib/derive-docs/tests/return_vec_box_sketch_group.gen b/src/wasm-lib/derive-docs/tests/return_vec_box_sketch_group.gen index 38ac2d2fa..6aab24172 100644 --- a/src/wasm-lib/derive-docs/tests/return_vec_box_sketch_group.gen +++ b/src/wasm-lib/derive-docs/tests/return_vec_box_sketch_group.gen @@ -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, - > + Send, + > + Send + + '_, >, > { - Box::pin(import(args)) + Box::pin(import(exec_state, args)) } impl crate::docs::StdLibFn for Import { diff --git a/src/wasm-lib/derive-docs/tests/return_vec_sketch_group.gen b/src/wasm-lib/derive-docs/tests/return_vec_sketch_group.gen index 6784e9122..dbfd8fdbd 100644 --- a/src/wasm-lib/derive-docs/tests/return_vec_sketch_group.gen +++ b/src/wasm-lib/derive-docs/tests/return_vec_sketch_group.gen @@ -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, - > + Send, + > + Send + + '_, >, > { - Box::pin(import(args)) + Box::pin(import(exec_state, args)) } impl crate::docs::StdLibFn for Import { diff --git a/src/wasm-lib/derive-docs/tests/show.gen b/src/wasm-lib/derive-docs/tests/show.gen index d0b5094bf..d908ffdad 100644 --- a/src/wasm-lib/derive-docs/tests/show.gen +++ b/src/wasm-lib/derive-docs/tests/show.gen @@ -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, - > + Send, + > + Send + + '_, >, > { - Box::pin(show(args)) + Box::pin(show(exec_state, args)) } impl crate::docs::StdLibFn for Show { diff --git a/src/wasm-lib/derive-docs/tests/test_args_with_exec_state.gen b/src/wasm-lib/derive-docs/tests/test_args_with_exec_state.gen new file mode 100644 index 000000000..61ada1040 --- /dev/null +++ b/src/wasm-lib/derive-docs/tests/test_args_with_exec_state.gen @@ -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, + > + 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 { + vec![] + } + + fn args(&self) -> Vec { + 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 { + 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: ::json_schema(&mut generator), + required: true, + }) + } + + fn unpublished(&self) -> bool { + false + } + + fn deprecated(&self) -> bool { + false + } + + fn examples(&self) -> Vec { + 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::>() + } + + fn std_lib_fn(&self) -> crate::std::StdFn { + boxed_some_function + } + + fn clone_box(&self) -> Box { + 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 +} diff --git a/src/wasm-lib/kcl/src/ast/types.rs b/src/wasm-lib/kcl/src/ast/types.rs index ea6d9b4ee..5911f1c2a 100644 --- a/src/wasm-lib/kcl/src/ast/types.rs +++ b/src/wasm-lib/kcl/src/ast/types.rs @@ -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 { + pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result { 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 { - let fn_name = self.callee.name.clone(); + pub async fn execute(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result { + let fn_name = &self.callee.name; let mut fn_args: Vec = 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 = 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 = 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 { + pub async fn execute(&self, exec_state: &mut ExecState) -> Result { 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 { + pub async fn execute(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result { 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 { + pub async fn execute(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result { 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 { + pub fn get_result_array(&self, exec_state: &mut ExecState, index: usize) -> Result { 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 { + pub fn get_result(&self, exec_state: &mut ExecState) -> Result { #[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 { - 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 { + 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 { + pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result { 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 { - 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 { + 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 { - 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 { 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, - pub params_optional: Vec, - pub body: Program, -} - #[derive(Debug, PartialEq, Clone)] pub struct RequiredParamAfterOptionalParam(pub Parameter); @@ -3472,36 +3326,28 @@ impl FunctionExpression { } }); - pub fn into_parts(self) -> Result { + 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. diff --git a/src/wasm-lib/kcl/src/executor.rs b/src/wasm-lib/kcl/src/executor.rs index fb94b739d..e42dae71c 100644 --- a/src/wasm-lib/kcl/src/executor.rs +++ b/src/wasm-lib/kcl/src/executor.rs @@ -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, +} + #[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, } @@ -730,25 +744,10 @@ pub type MemoryFunction = memory: ProgramMemory, expression: Box, metadata: Vec, - dynamic_state: DynamicState, + exec_state: &ExecState, ctx: ExecutorContext, ) -> std::pin::Pin, KclError>> + Send>>; -fn force_memory_function< - F: Fn( - Vec, - ProgramMemory, - Box, - Vec, - DynamicState, - ExecutorContext, - ) -> std::pin::Pin, KclError>> + Send>>, ->( - f: F, -) -> F { - f -} - impl From for Vec { 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, - dynamic_state: &DynamicState, + exec_state: &mut ExecState, ctx: ExecutorContext, ) -> Result, 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, -} - -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, - ) -> Result { + ) -> Result { 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, - ) -> Result<(ProgramMemory, Option), KclError> { + ) -> Result<(ExecState, Option), 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 { - 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 { 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, - memory: ProgramMemory, - function_expression: Box, - _metadata: Vec, - 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, + memory: &ProgramMemory, + function_expression: &FunctionExpression, + exec_state: &mut ExecState, + ctx: &ExecutorContext, +) -> Result, 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. diff --git a/src/wasm-lib/kcl/src/function_param.rs b/src/wasm-lib/kcl/src/function_param.rs index b5d44ccea..63437e23b 100644 --- a/src/wasm-lib/kcl/src/function_param.rs +++ b/src/wasm-lib/kcl/src/function_param.rs @@ -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, pub meta: Vec, pub ctx: ExecutorContext, } impl<'a> FunctionParam<'a> { - pub async fn call(&self, args: Vec) -> Result, 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) -> Result, 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 + } } } diff --git a/src/wasm-lib/kcl/src/lsp/kcl/mod.rs b/src/wasm-lib/kcl/src/lsp/kcl/mod.rs index 02410e897..45215c096 100644 --- a/src/wasm-lib/kcl/src/lsp/kcl/mod.rs +++ b/src/wasm-lib/kcl/src/lsp/kcl/mod.rs @@ -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::(memory) + .send_notification::(exec_state.memory) .await; Ok(()) diff --git a/src/wasm-lib/kcl/src/std/args.rs b/src/wasm-lib/kcl/src/std/args.rs index d34f47808..425c66540 100644 --- a/src/wasm-lib/kcl/src/std/args.rs +++ b/src/wasm-lib/kcl/src/std/args.rs @@ -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, pub source_range: SourceRange, pub ctx: ExecutorContext, - pub current_program_memory: ProgramMemory, - pub dynamic_state: DynamicState, } impl Args { - pub fn new( - args: Vec, - source_range: SourceRange, - ctx: ExecutorContext, - current_program_memory: ProgramMemory, - dynamic_state: DynamicState, - ) -> Self { + pub fn new(args: Vec, 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>, ) -> 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 { @@ -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 { diff --git a/src/wasm-lib/kcl/src/std/array.rs b/src/wasm-lib/kcl/src/std/array.rs index c1836769c..e8f3c68aa 100644 --- a/src/wasm-lib/kcl/src/std/array.rs +++ b/src/wasm-lib/kcl/src/std/array.rs @@ -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 { +pub async fn array_reduce(exec_state: &mut ExecState, args: Args) -> Result { let (array, start, f): (Vec, 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 { 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, start: SketchGroup, reduce_fn: FunctionParam<'a>, + exec_state: &mut ExecState, args: &'a Args, ) -> Result { 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 { // 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]; diff --git a/src/wasm-lib/kcl/src/std/assert.rs b/src/wasm-lib/kcl/src/std/assert.rs index 299838188..8f430bc14 100644 --- a/src/wasm-lib/kcl/src/std/assert.rs +++ b/src/wasm-lib/kcl/src/std/assert.rs @@ -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 { +pub async fn assert(_exec_state: &mut ExecState, args: Args) -> Result { 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 { +pub async fn assert_lt(_exec_state: &mut ExecState, args: Args) -> Result { 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 { +pub async fn assert_gt(_exec_state: &mut ExecState, args: Args) -> Result { 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 { +pub async fn assert_equal(_exec_state: &mut ExecState, args: Args) -> Result { 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 { +pub async fn assert_lte(_exec_state: &mut ExecState, args: Args) -> Result { 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 { +pub async fn assert_gte(_exec_state: &mut ExecState, args: Args) -> Result { let (left, right, description): (f64, f64, String) = args.get_data()?; inner_assert_gte(left, right, &description, &args).await?; args.make_null_user_val() diff --git a/src/wasm-lib/kcl/src/std/chamfer.rs b/src/wasm-lib/kcl/src/std/chamfer.rs index 5108a7d98..f06f88ddf 100644 --- a/src/wasm-lib/kcl/src/std/chamfer.rs +++ b/src/wasm-lib/kcl/src/std/chamfer.rs @@ -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 { +pub async fn chamfer(exec_state: &mut ExecState, args: Args) -> Result { let (data, extrude_group, tag): (ChamferData, Box, Option) = 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, tag: Option, + exec_state: &mut ExecState, args: Args, ) -> Result, 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(); diff --git a/src/wasm-lib/kcl/src/std/convert.rs b/src/wasm-lib/kcl/src/std/convert.rs index 8ce353b4f..52e73b08c 100644 --- a/src/wasm-lib/kcl/src/std/convert.rs +++ b/src/wasm-lib/kcl/src/std/convert.rs @@ -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 { +pub async fn int(_exec_state: &mut ExecState, args: Args) -> Result { let num = args.get_number()?; let converted = inner_int(num).map_err(|err| err.into_kcl_error(args.source_range))?; diff --git a/src/wasm-lib/kcl/src/std/extrude.rs b/src/wasm-lib/kcl/src/std/extrude.rs index 4af84bfa7..2abba6822 100644 --- a/src/wasm-lib/kcl/src/std/extrude.rs +++ b/src/wasm-lib/kcl/src/std/extrude.rs @@ -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 { +pub async fn extrude(_exec_state: &mut ExecState, args: Args) -> Result { let (length, sketch_group_set) = args.get_number_sketch_group_set()?; let result = inner_extrude(length, sketch_group_set, args).await?; diff --git a/src/wasm-lib/kcl/src/std/fillet.rs b/src/wasm-lib/kcl/src/std/fillet.rs index fd4cb7692..2d26b4003 100644 --- a/src/wasm-lib/kcl/src/std/fillet.rs +++ b/src/wasm-lib/kcl/src/std/fillet.rs @@ -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 { +pub async fn fillet(exec_state: &mut ExecState, args: Args) -> Result { let (data, extrude_group, tag): (FilletData, Box, Option) = 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, tag: Option, + exec_state: &mut ExecState, args: Args, ) -> Result, 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 { +pub async fn get_opposite_edge(exec_state: &mut ExecState, args: Args) -> Result { 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 { #[stdlib { name = "getOppositeEdge", }] -async fn inner_get_opposite_edge(tag: TagIdentifier, args: Args) -> Result { +async fn inner_get_opposite_edge(tag: TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result { 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 Result { +pub async fn get_next_adjacent_edge(exec_state: &mut ExecState, args: Args) -> Result { 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 { #[stdlib { name = "getNextAdjacentEdge", }] -async fn inner_get_next_adjacent_edge(tag: TagIdentifier, args: Args) -> Result { +async fn inner_get_next_adjacent_edge( + tag: TagIdentifier, + exec_state: &mut ExecState, + args: Args, +) -> Result { 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 { +pub async fn get_previous_adjacent_edge(exec_state: &mut ExecState, args: Args) -> Result { 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 Result { +async fn inner_get_previous_adjacent_edge( + tag: TagIdentifier, + exec_state: &mut ExecState, + args: Args, +) -> Result { 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( diff --git a/src/wasm-lib/kcl/src/std/helix.rs b/src/wasm-lib/kcl/src/std/helix.rs index 220bf650b..574490916 100644 --- a/src/wasm-lib/kcl/src/std/helix.rs +++ b/src/wasm-lib/kcl/src/std/helix.rs @@ -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 { +pub async fn helix(_exec_state: &mut ExecState, args: Args) -> Result { let (data, extrude_group): (HelixData, Box) = args.get_data_and_extrude_group()?; let extrude_group = inner_helix(data, extrude_group, args).await?; diff --git a/src/wasm-lib/kcl/src/std/import.rs b/src/wasm-lib/kcl/src/std/import.rs index 2d7fed754..b883b2b95 100644 --- a/src/wasm-lib/kcl/src/std/import.rs +++ b/src/wasm-lib/kcl/src/std/import.rs @@ -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 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 { +pub async fn import(_exec_state: &mut ExecState, args: Args) -> Result { let (file_path, options): (String, Option) = args.get_import_data()?; let imported_geometry = inner_import(file_path, options, args).await?; diff --git a/src/wasm-lib/kcl/src/std/loft.rs b/src/wasm-lib/kcl/src/std/loft.rs index 57cf8f16d..ea14593f3 100644 --- a/src/wasm-lib/kcl/src/std/loft.rs +++ b/src/wasm-lib/kcl/src/std/loft.rs @@ -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 { +pub async fn loft(_exec_state: &mut ExecState, args: Args) -> Result { let (sketch_groups, data): (Vec, Option) = args.get_sketch_groups_and_data()?; let extrude_group = inner_loft(sketch_groups, data, args).await?; diff --git a/src/wasm-lib/kcl/src/std/math.rs b/src/wasm-lib/kcl/src/std/math.rs index d53307c17..e7c43e614 100644 --- a/src/wasm-lib/kcl/src/std/math.rs +++ b/src/wasm-lib/kcl/src/std/math.rs @@ -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 { +pub async fn cos(_exec_state: &mut ExecState, args: Args) -> Result { let num = args.get_number()?; let result = inner_cos(num)?; @@ -41,7 +41,7 @@ fn inner_cos(num: f64) -> Result { } /// Compute the sine of a number (in radians). -pub async fn sin(args: Args) -> Result { +pub async fn sin(_exec_state: &mut ExecState, args: Args) -> Result { let num = args.get_number()?; let result = inner_sin(num)?; @@ -71,7 +71,7 @@ fn inner_sin(num: f64) -> Result { } /// Compute the tangent of a number (in radians). -pub async fn tan(args: Args) -> Result { +pub async fn tan(_exec_state: &mut ExecState, args: Args) -> Result { let num = args.get_number()?; let result = inner_tan(num)?; @@ -101,7 +101,7 @@ fn inner_tan(num: f64) -> Result { } /// Return the value of `pi`. Archimedes’ constant (π). -pub async fn pi(args: Args) -> Result { +pub async fn pi(_exec_state: &mut ExecState, args: Args) -> Result { let result = inner_pi()?; args.make_user_val_from_f64(result) @@ -126,7 +126,7 @@ fn inner_pi() -> Result { } /// Compute the square root of a number. -pub async fn sqrt(args: Args) -> Result { +pub async fn sqrt(_exec_state: &mut ExecState, args: Args) -> Result { let num = args.get_number()?; let result = inner_sqrt(num)?; @@ -156,7 +156,7 @@ fn inner_sqrt(num: f64) -> Result { } /// Compute the absolute value of a number. -pub async fn abs(args: Args) -> Result { +pub async fn abs(_exec_state: &mut ExecState, args: Args) -> Result { let num = args.get_number()?; let result = inner_abs(num)?; @@ -193,7 +193,7 @@ fn inner_abs(num: f64) -> Result { } /// Compute the largest integer less than or equal to a number. -pub async fn floor(args: Args) -> Result { +pub async fn floor(_exec_state: &mut ExecState, args: Args) -> Result { let num = args.get_number()?; let result = inner_floor(num)?; @@ -221,7 +221,7 @@ fn inner_floor(num: f64) -> Result { } /// Compute the smallest integer greater than or equal to a number. -pub async fn ceil(args: Args) -> Result { +pub async fn ceil(_exec_state: &mut ExecState, args: Args) -> Result { let num = args.get_number()?; let result = inner_ceil(num)?; @@ -249,7 +249,7 @@ fn inner_ceil(num: f64) -> Result { } /// Compute the minimum of the given arguments. -pub async fn min(args: Args) -> Result { +pub async fn min(_exec_state: &mut ExecState, args: Args) -> Result { let nums = args.get_number_array()?; let result = inner_min(nums); @@ -286,7 +286,7 @@ fn inner_min(args: Vec) -> f64 { } /// Compute the maximum of the given arguments. -pub async fn max(args: Args) -> Result { +pub async fn max(_exec_state: &mut ExecState, args: Args) -> Result { let nums = args.get_number_array()?; let result = inner_max(nums); @@ -323,7 +323,7 @@ fn inner_max(args: Vec) -> f64 { } /// Compute the number to a power. -pub async fn pow(args: Args) -> Result { +pub async fn pow(_exec_state: &mut ExecState, args: Args) -> Result { 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 { } /// Compute the arccosine of a number (in radians). -pub async fn acos(args: Args) -> Result { +pub async fn acos(_exec_state: &mut ExecState, args: Args) -> Result { let num = args.get_number()?; let result = inner_acos(num)?; @@ -398,7 +398,7 @@ fn inner_acos(num: f64) -> Result { } /// Compute the arcsine of a number (in radians). -pub async fn asin(args: Args) -> Result { +pub async fn asin(_exec_state: &mut ExecState, args: Args) -> Result { let num = args.get_number()?; let result = inner_asin(num)?; @@ -428,7 +428,7 @@ fn inner_asin(num: f64) -> Result { } /// Compute the arctangent of a number (in radians). -pub async fn atan(args: Args) -> Result { +pub async fn atan(_exec_state: &mut ExecState, args: Args) -> Result { let num = args.get_number()?; let result = inner_atan(num)?; @@ -462,7 +462,7 @@ fn inner_atan(num: f64) -> Result { /// 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 { +pub async fn log(_exec_state: &mut ExecState, args: Args) -> Result { 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 { } /// Compute the base 2 logarithm of the number. -pub async fn log2(args: Args) -> Result { +pub async fn log2(_exec_state: &mut ExecState, args: Args) -> Result { let num = args.get_number()?; let result = inner_log2(num)?; @@ -535,7 +535,7 @@ fn inner_log2(num: f64) -> Result { } /// Compute the base 10 logarithm of the number. -pub async fn log10(args: Args) -> Result { +pub async fn log10(_exec_state: &mut ExecState, args: Args) -> Result { let num = args.get_number()?; let result = inner_log10(num)?; @@ -563,7 +563,7 @@ fn inner_log10(num: f64) -> Result { } /// Compute the natural logarithm of the number. -pub async fn ln(args: Args) -> Result { +pub async fn ln(_exec_state: &mut ExecState, args: Args) -> Result { let num = args.get_number()?; let result = inner_ln(num)?; @@ -591,7 +591,7 @@ fn inner_ln(num: f64) -> Result { } /// Return the value of Euler’s number `e`. -pub async fn e(args: Args) -> Result { +pub async fn e(_exec_state: &mut ExecState, args: Args) -> Result { let result = inner_e()?; args.make_user_val_from_f64(result) @@ -620,7 +620,7 @@ fn inner_e() -> Result { } /// Return the value of `tau`. The full circle constant (τ). Equal to 2π. -pub async fn tau(args: Args) -> Result { +pub async fn tau(_exec_state: &mut ExecState, args: Args) -> Result { let result = inner_tau()?; args.make_user_val_from_f64(result) @@ -649,7 +649,7 @@ fn inner_tau() -> Result { } /// Converts a number from degrees to radians. -pub async fn to_radians(args: Args) -> Result { +pub async fn to_radians(_exec_state: &mut ExecState, args: Args) -> Result { let num = args.get_number()?; let result = inner_to_radians(num)?; @@ -679,7 +679,7 @@ fn inner_to_radians(num: f64) -> Result { } /// Converts a number from radians to degrees. -pub async fn to_degrees(args: Args) -> Result { +pub async fn to_degrees(_exec_state: &mut ExecState, args: Args) -> Result { let num = args.get_number()?; let result = inner_to_degrees(num)?; diff --git a/src/wasm-lib/kcl/src/std/mod.rs b/src/wasm-lib/kcl/src/std/mod.rs index 89f4f76cd..89af8d5c3 100644 --- a/src/wasm-lib/kcl/src/std/mod.rs +++ b/src/wasm-lib/kcl/src/std/mod.rs @@ -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> + Send>>; +pub type StdFn = fn( + &mut ExecState, + Args, +) -> std::pin::Pin> + Send + '_>>; pub type FnMap = HashMap; @@ -228,7 +231,7 @@ pub enum FunctionKind { } /// Compute the length of the given leg. -pub async fn leg_length(args: Args) -> Result { +pub async fn leg_length(_exec_state: &mut ExecState, args: Args) -> Result { 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 { +pub async fn leg_angle_x(_exec_state: &mut ExecState, args: Args) -> Result { 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 { +pub async fn leg_angle_y(_exec_state: &mut ExecState, args: Args) -> Result { 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, pub memory: Box, } diff --git a/src/wasm-lib/kcl/src/std/patterns.rs b/src/wasm-lib/kcl/src/std/patterns.rs index 7e10849f1..532d4883c 100644 --- a/src/wasm-lib/kcl/src/std/patterns.rs +++ b/src/wasm-lib/kcl/src/std/patterns.rs @@ -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 { +pub async fn pattern_transform(exec_state: &mut ExecState, args: Args) -> Result { 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 { 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>, 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> = 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 { // 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 { +pub async fn pattern_linear_2d(_exec_state: &mut ExecState, args: Args) -> Result { 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 { +pub async fn pattern_linear_3d(exec_state: &mut ExecState, args: Args) -> Result { 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 { })); } - 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 { async fn inner_pattern_linear_3d( data: LinearPattern3dData, extrude_group_set: ExtrudeGroupSet, + exec_state: &mut ExecState, args: Args, ) -> Result>, 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> = 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 { +pub async fn pattern_circular_2d(_exec_state: &mut ExecState, args: Args) -> Result { 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 { +pub async fn pattern_circular_3d(exec_state: &mut ExecState, args: Args) -> Result { 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 { async fn inner_pattern_circular_3d( data: CircularPattern3dData, extrude_group_set: ExtrudeGroupSet, + exec_state: &mut ExecState, args: Args, ) -> Result>, 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> = extrude_group_set.into(); diff --git a/src/wasm-lib/kcl/src/std/planes.rs b/src/wasm-lib/kcl/src/std/planes.rs index 47ad5c379..13c546f1c 100644 --- a/src/wasm-lib/kcl/src/std/planes.rs +++ b/src/wasm-lib/kcl/src/std/planes.rs @@ -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 for PlaneData { } /// Offset a plane by a distance along its normal. -pub async fn offset_plane(args: Args) -> Result { +pub async fn offset_plane(_exec_state: &mut ExecState, args: Args) -> Result { let (std_plane, offset): (StandardPlane, f64) = args.get_data_and_float()?; let plane = inner_offset_plane(std_plane, offset).await?; diff --git a/src/wasm-lib/kcl/src/std/polar.rs b/src/wasm-lib/kcl/src/std/polar.rs index 5385f0e11..f29be1c0c 100644 --- a/src/wasm-lib/kcl/src/std/polar.rs +++ b/src/wasm-lib/kcl/src/std/polar.rs @@ -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 { +pub async fn polar(_exec_state: &mut ExecState, args: Args) -> Result { let data: PolarCoordsData = args.get_data()?; let result = inner_polar(&data)?; diff --git a/src/wasm-lib/kcl/src/std/revolve.rs b/src/wasm-lib/kcl/src/std/revolve.rs index ffdffc7a4..a4683c7ab 100644 --- a/src/wasm-lib/kcl/src/std/revolve.rs +++ b/src/wasm-lib/kcl/src/std/revolve.rs @@ -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 { +pub async fn revolve(exec_state: &mut ExecState, args: Args) -> Result { 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 { async fn inner_revolve( data: RevolveData, sketch_group: SketchGroup, + exec_state: &mut ExecState, args: Args, ) -> Result, 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, diff --git a/src/wasm-lib/kcl/src/std/segment.rs b/src/wasm-lib/kcl/src/std/segment.rs index fed858a55..297439500 100644 --- a/src/wasm-lib/kcl/src/std/segment.rs +++ b/src/wasm-lib/kcl/src/std/segment.rs @@ -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 { +pub async fn segment_end_x(exec_state: &mut ExecState, args: Args) -> Result { 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 { #[stdlib { name = "segEndX", }] -fn inner_segment_end_x(tag: &TagIdentifier, args: Args) -> Result { - let line = args.get_tag_engine_info(tag)?; +fn inner_segment_end_x(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result { + 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 } /// Returns the segment end of y. -pub async fn segment_end_y(args: Args) -> Result { +pub async fn segment_end_y(exec_state: &mut ExecState, args: Args) -> Result { 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 { #[stdlib { name = "segEndY", }] -fn inner_segment_end_y(tag: &TagIdentifier, args: Args) -> Result { - let line = args.get_tag_engine_info(tag)?; +fn inner_segment_end_y(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result { + 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 } /// Returns the last segment of x. -pub async fn last_segment_x(args: Args) -> Result { +pub async fn last_segment_x(_exec_state: &mut ExecState, args: Args) -> Result { 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 Result { +pub async fn last_segment_y(_exec_state: &mut ExecState, args: Args) -> Result { 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 Result { +pub async fn segment_length(exec_state: &mut ExecState, args: Args) -> Result { 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 { #[stdlib { name = "segLen", }] -fn inner_segment_length(tag: &TagIdentifier, args: Args) -> Result { - let line = args.get_tag_engine_info(tag)?; +fn inner_segment_length(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result { + 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 Result { +pub async fn segment_angle(exec_state: &mut ExecState, args: Args) -> Result { 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 { #[stdlib { name = "segAng", }] -fn inner_segment_angle(tag: &TagIdentifier, args: Args) -> Result { - let line = args.get_tag_engine_info(tag)?; +fn inner_segment_angle(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result { + 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 } /// Returns the angle to match the given length for x. -pub async fn angle_to_match_length_x(args: Args) -> Result { +pub async fn angle_to_match_length_x(exec_state: &mut ExecState, args: Args) -> Result { 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 { - 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 { +pub async fn angle_to_match_length_y(exec_state: &mut ExecState, args: Args) -> Result { 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 { - 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), diff --git a/src/wasm-lib/kcl/src/std/shapes.rs b/src/wasm-lib/kcl/src/std/shapes.rs index 1c80c2d9d..149181452 100644 --- a/src/wasm-lib/kcl/src/std/shapes.rs +++ b/src/wasm-lib/kcl/src/std/shapes.rs @@ -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 { +pub async fn circle(exec_state: &mut ExecState, args: Args) -> Result { let (center, radius, sketch_surface_or_group, tag): ([f64; 2], f64, SketchSurfaceOrGroup, Option) = 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, + exec_state: &mut ExecState, args: Args, ) -> Result { 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( diff --git a/src/wasm-lib/kcl/src/std/shell.rs b/src/wasm-lib/kcl/src/std/shell.rs index c98824b06..94ac0382a 100644 --- a/src/wasm-lib/kcl/src/std/shell.rs +++ b/src/wasm-lib/kcl/src/std/shell.rs @@ -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 { +pub async fn shell(exec_state: &mut ExecState, args: Args) -> Result { let (data, extrude_group): (ShellData, Box) = 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 { async fn inner_shell( data: ShellData, extrude_group: Box, + exec_state: &mut ExecState, args: Args, ) -> Result, 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 { +pub async fn hollow(exec_state: &mut ExecState, args: Args) -> Result { let (thickness, extrude_group): (f64, Box) = 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 { async fn inner_hollow( thickness: f64, extrude_group: Box, + exec_state: &mut ExecState, args: Args, ) -> Result, 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( diff --git a/src/wasm-lib/kcl/src/std/sketch.rs b/src/wasm-lib/kcl/src/std/sketch.rs index e62cc719e..cb545184d 100644 --- a/src/wasm-lib/kcl/src/std/sketch.rs +++ b/src/wasm-lib/kcl/src/std/sketch.rs @@ -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 { 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 { +pub async fn line_to(_exec_state: &mut ExecState, args: Args) -> Result { let (to, sketch_group, tag): ([f64; 2], SketchGroup, Option) = 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 { +pub async fn x_line_to(_exec_state: &mut ExecState, args: Args) -> Result { let (to, sketch_group, tag): (f64, SketchGroup, Option) = 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 { +pub async fn y_line_to(_exec_state: &mut ExecState, args: Args) -> Result { let (to, sketch_group, tag): (f64, SketchGroup, Option) = 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 { +pub async fn line(_exec_state: &mut ExecState, args: Args) -> Result { let (delta, sketch_group, tag): ([f64; 2], SketchGroup, Option) = 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 { +pub async fn x_line(_exec_state: &mut ExecState, args: Args) -> Result { let (length, sketch_group, tag): (f64, SketchGroup, Option) = 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 { +pub async fn y_line(_exec_state: &mut ExecState, args: Args) -> Result { let (length, sketch_group, tag): (f64, SketchGroup, Option) = 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 { +pub async fn angled_line(_exec_state: &mut ExecState, args: Args) -> Result { let (data, sketch_group, tag): (AngledLineData, SketchGroup, Option) = 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 { +pub async fn angled_line_of_x_length(_exec_state: &mut ExecState, args: Args) -> Result { let (data, sketch_group, tag): (AngledLineData, SketchGroup, Option) = 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 { +pub async fn angled_line_to_x(_exec_state: &mut ExecState, args: Args) -> Result { let (data, sketch_group, tag): (AngledLineToData, SketchGroup, Option) = 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 { +pub async fn angled_line_of_y_length(_exec_state: &mut ExecState, args: Args) -> Result { let (data, sketch_group, tag): (AngledLineData, SketchGroup, Option) = 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 { +pub async fn angled_line_to_y(_exec_state: &mut ExecState, args: Args) -> Result { let (data, sketch_group, tag): (AngledLineToData, SketchGroup, Option) = 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 { +pub async fn angled_line_that_intersects(exec_state: &mut ExecState, args: Args) -> Result { let (data, sketch_group, tag): (AngledLineThatIntersectsData, SketchGroup, Option) = 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, + exec_state: &mut ExecState, args: Args, ) -> Result { - 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 { +pub async fn start_sketch_at(exec_state: &mut ExecState, args: Args) -> Result { 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 { #[stdlib { name = "startSketchAt", }] -async fn inner_start_sketch_at(data: [f64; 2], args: Args) -> Result { +async fn inner_start_sketch_at( + data: [f64; 2], + exec_state: &mut ExecState, + args: Args, +) -> Result { // 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 for Plane { } /// Start a sketch on a specific plane or face. -pub async fn start_sketch_on(args: Args) -> Result { +pub async fn start_sketch_on(exec_state: &mut ExecState, args: Args) -> Result { let (data, tag): (SketchData, Option) = 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 { #[stdlib { name = "startSketchOn", }] -async fn inner_start_sketch_on(data: SketchData, tag: Option, args: Args) -> Result { +async fn inner_start_sketch_on( + data: SketchData, + tag: Option, + exec_state: &mut ExecState, + args: &Args, +) -> Result { 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, 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, args: Arg async fn start_sketch_on_face( extrude_group: Box, tag: FaceTag, - args: Args, + exec_state: &mut ExecState, + args: &Args, ) -> Result, 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, KclError> { +async fn start_sketch_on_plane(data: PlaneData, args: &Args) -> Result, 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 } /// Start a new profile at a given point. -pub async fn start_profile_at(args: Args) -> Result { +pub async fn start_profile_at(exec_state: &mut ExecState, args: Args) -> Result { let (start, sketch_surface, tag): ([f64; 2], SketchSurface, Option) = 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, + exec_state: &mut ExecState, args: Args, ) -> Result { 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 { +pub async fn profile_start_x(_exec_state: &mut ExecState, args: Args) -> Result { 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 Result { +pub async fn profile_start_y(_exec_state: &mut ExecState, args: Args) -> Result { 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 Result { +pub async fn profile_start(_exec_state: &mut ExecState, args: Args) -> Result { 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 { +pub async fn close(_exec_state: &mut ExecState, args: Args) -> Result { let (sketch_group, tag): (SketchGroup, Option) = 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 { +pub async fn arc(_exec_state: &mut ExecState, args: Args) -> Result { let (data, sketch_group, tag): (ArcData, SketchGroup, Option) = 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 { +pub async fn tangential_arc(_exec_state: &mut ExecState, args: Args) -> Result { let (data, sketch_group, tag): (TangentialArcData, SketchGroup, Option) = 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 { +pub async fn tangential_arc_to(_exec_state: &mut ExecState, args: Args) -> Result { let (to, sketch_group, tag): ([f64; 2], SketchGroup, Option) = super::args::FromArgs::from_args(&args, 0)?; @@ -1776,7 +1789,7 @@ pub async fn tangential_arc_to(args: Args) -> Result { } /// Draw a tangential arc to point some distance away.. -pub async fn tangential_arc_to_relative(args: Args) -> Result { +pub async fn tangential_arc_to_relative(_exec_state: &mut ExecState, args: Args) -> Result { let (delta, sketch_group, tag): ([f64; 2], SketchGroup, Option) = 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 { +pub async fn bezier_curve(_exec_state: &mut ExecState, args: Args) -> Result { let (data, sketch_group, tag): (BezierData, SketchGroup, Option) = 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 { +pub async fn hole(_exec_state: &mut ExecState, args: Args) -> Result { 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?; diff --git a/src/wasm-lib/kcl/src/std/units.rs b/src/wasm-lib/kcl/src/std/units.rs index 89350864f..262554b7f 100644 --- a/src/wasm-lib/kcl/src/std/units.rs +++ b/src/wasm-lib/kcl/src/std/units.rs @@ -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 { +pub async fn mm(_exec_state: &mut ExecState, args: Args) -> Result { let result = inner_mm(&args)?; args.make_user_val_from_f64(result) @@ -48,7 +53,7 @@ fn inner_mm(args: &Args) -> Result { } /// Inches conversion factor for current projects units. -pub async fn inch(args: Args) -> Result { +pub async fn inch(_exec_state: &mut ExecState, args: Args) -> Result { let result = inner_inch(&args)?; args.make_user_val_from_f64(result) @@ -89,7 +94,7 @@ fn inner_inch(args: &Args) -> Result { } /// Feet conversion factor for current projects units. -pub async fn ft(args: Args) -> Result { +pub async fn ft(_exec_state: &mut ExecState, args: Args) -> Result { let result = inner_ft(&args)?; args.make_user_val_from_f64(result) @@ -131,7 +136,7 @@ fn inner_ft(args: &Args) -> Result { } /// Meters conversion factor for current projects units. -pub async fn m(args: Args) -> Result { +pub async fn m(_exec_state: &mut ExecState, args: Args) -> Result { let result = inner_m(&args)?; args.make_user_val_from_f64(result) @@ -173,7 +178,7 @@ fn inner_m(args: &Args) -> Result { } /// Centimeters conversion factor for current projects units. -pub async fn cm(args: Args) -> Result { +pub async fn cm(_exec_state: &mut ExecState, args: Args) -> Result { let result = inner_cm(&args)?; args.make_user_val_from_f64(result) @@ -215,7 +220,7 @@ fn inner_cm(args: &Args) -> Result { } /// Yards conversion factor for current projects units. -pub async fn yd(args: Args) -> Result { +pub async fn yd(_exec_state: &mut ExecState, args: Args) -> Result { let result = inner_yd(&args)?; args.make_user_val_from_f64(result) diff --git a/src/wasm-lib/src/wasm.rs b/src/wasm-lib/src/wasm.rs index 4550424d3..79ffa9bff 100644 --- a/src/wasm-lib/src/wasm.rs +++ b/src/wasm-lib/src/wasm.rs @@ -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 diff --git a/src/wasm-lib/tests/executor/inputs/no_visuals/pipe_substitution_inside_function_called_from_pipeline.kcl b/src/wasm-lib/tests/executor/inputs/no_visuals/pipe_substitution_inside_function_called_from_pipeline.kcl new file mode 100644 index 000000000..8be546a18 --- /dev/null +++ b/src/wasm-lib/tests/executor/inputs/no_visuals/pipe_substitution_inside_function_called_from_pipeline.kcl @@ -0,0 +1,7 @@ +// Make sure pipe value doesn't leak into the function call. +fn f = (ignored) => { + return % +} + +const answer = % + |> f(%) diff --git a/src/wasm-lib/tests/executor/no_visuals.rs b/src/wasm-lib/tests/executor/no_visuals.rs index 2d91fdc3b..3fb7137bd 100644 --- a/src/wasm-lib/tests/executor/no_visuals.rs +++ b/src/wasm-lib/tests/executor/no_visuals.rs @@ -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); diff --git a/src/wasm-lib/tests/modify/main.rs b/src/wasm-lib/tests/modify/main.rs index b075485ec..680367835 100644 --- a/src/wasm-lib/tests/modify/main.rs +++ b/src/wasm-lib/tests/modify/main.rs @@ -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::() else { anyhow::bail!("part001 was not a SketchGroup");