Unify execution state into a single struct (#3877)

* Add ExecState that combines ProgramMemory and DynamicState

* Remove unneeded clones

* Add exec_state parameter to all KCL stdlib functions

* Move pipe value into ExecState

* Add test for pipe substitution not leaking into function calls

* KCL: Better message on assertEqual function

Also add a new no-visual test for performance testing.

* Fix new array module to use ExecState

---------

Co-authored-by: Adam Chalmers <adam.chalmers@zoo.dev>
This commit is contained in:
Jonathan Tran
2024-09-16 15:10:33 -04:00
committed by GitHub
parent c4ff1c2ef1
commit 0ff820d4da
44 changed files with 765 additions and 709 deletions

View File

@ -269,7 +269,7 @@ fn do_stdlib_inner(
let ty_string = rust_type_to_openapi_type(&ty_string);
let required = !ty_ident.to_string().starts_with("Option <");
if ty_string != "Args" {
if ty_string != "ExecState" && ty_string != "Args" {
let schema = if ty_ident.to_string().starts_with("Vec < ")
|| ty_ident.to_string().starts_with("Option <")
|| ty_ident.to_string().starts_with('[')
@ -387,11 +387,12 @@ fn do_stdlib_inner(
#const_struct
fn #boxed_fn_name_ident(
exec_state: &mut crate::executor::ExecState,
args: crate::std::Args,
) -> std::pin::Pin<
Box<dyn std::future::Future<Output = anyhow::Result<crate::executor::KclValue, crate::errors::KclError>> + Send>,
Box<dyn std::future::Future<Output = anyhow::Result<crate::executor::KclValue, crate::errors::KclError>> + Send + '_>,
> {
Box::pin(#fn_name_ident(args))
Box::pin(#fn_name_ident(exec_state, args))
}
impl #docs_crate::StdLibFn for #name_ident
@ -662,6 +663,9 @@ fn clean_ty_string(t: &str) -> (String, proc_macro2::TokenStream) {
.replace("mut", "")
.replace("< 'a >", "")
.replace(' ', "");
if ty_string.starts_with("ExecState") {
ty_string = "ExecState".to_string();
}
if ty_string.starts_with("Args") {
ty_string = "Args".to_string();
}

View File

@ -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(

View File

@ -44,15 +44,17 @@ pub(crate) struct SomeFn {}
#[doc = "Std lib function: someFn\nDocs"]
pub(crate) const SomeFn: SomeFn = SomeFn {};
fn boxed_someFn(
exec_state: &mut crate::executor::ExecState,
args: crate::std::Args,
) -> std::pin::Pin<
Box<
dyn std::future::Future<
Output = anyhow::Result<crate::executor::KclValue, crate::errors::KclError>,
> + Send,
> + Send
+ '_,
>,
> {
Box::pin(someFn(args))
Box::pin(someFn(exec_state, args))
}
impl crate::docs::StdLibFn for SomeFn {

View File

@ -44,15 +44,17 @@ pub(crate) struct SomeFn {}
#[doc = "Std lib function: someFn\nDocs"]
pub(crate) const SomeFn: SomeFn = SomeFn {};
fn boxed_someFn(
exec_state: &mut crate::executor::ExecState,
args: crate::std::Args,
) -> std::pin::Pin<
Box<
dyn std::future::Future<
Output = anyhow::Result<crate::executor::KclValue, crate::errors::KclError>,
> + Send,
> + Send
+ '_,
>,
> {
Box::pin(someFn(args))
Box::pin(someFn(exec_state, args))
}
impl crate::docs::StdLibFn for SomeFn {

View File

@ -77,15 +77,17 @@ pub(crate) struct Show {}
#[doc = "Std lib function: show\nThis is some function.\nIt does shit."]
pub(crate) const Show: Show = Show {};
fn boxed_show(
exec_state: &mut crate::executor::ExecState,
args: crate::std::Args,
) -> std::pin::Pin<
Box<
dyn std::future::Future<
Output = anyhow::Result<crate::executor::KclValue, crate::errors::KclError>,
> + Send,
> + Send
+ '_,
>,
> {
Box::pin(show(args))
Box::pin(show(exec_state, args))
}
impl crate::docs::StdLibFn for Show {

View File

@ -44,15 +44,17 @@ pub(crate) struct Show {}
#[doc = "Std lib function: show\nThis is some function.\nIt does shit."]
pub(crate) const Show: Show = Show {};
fn boxed_show(
exec_state: &mut crate::executor::ExecState,
args: crate::std::Args,
) -> std::pin::Pin<
Box<
dyn std::future::Future<
Output = anyhow::Result<crate::executor::KclValue, crate::errors::KclError>,
> + Send,
> + Send
+ '_,
>,
> {
Box::pin(show(args))
Box::pin(show(exec_state, args))
}
impl crate::docs::StdLibFn for Show {

View File

@ -78,15 +78,17 @@ pub(crate) struct MyFunc {}
#[doc = "Std lib function: myFunc\nThis is some function.\nIt does shit."]
pub(crate) const MyFunc: MyFunc = MyFunc {};
fn boxed_my_func(
exec_state: &mut crate::executor::ExecState,
args: crate::std::Args,
) -> std::pin::Pin<
Box<
dyn std::future::Future<
Output = anyhow::Result<crate::executor::KclValue, crate::errors::KclError>,
> + Send,
> + Send
+ '_,
>,
> {
Box::pin(my_func(args))
Box::pin(my_func(exec_state, args))
}
impl crate::docs::StdLibFn for MyFunc {

View File

@ -78,15 +78,17 @@ pub(crate) struct LineTo {}
#[doc = "Std lib function: lineTo\nThis is some function.\nIt does shit."]
pub(crate) const LineTo: LineTo = LineTo {};
fn boxed_line_to(
exec_state: &mut crate::executor::ExecState,
args: crate::std::Args,
) -> std::pin::Pin<
Box<
dyn std::future::Future<
Output = anyhow::Result<crate::executor::KclValue, crate::errors::KclError>,
> + Send,
> + Send
+ '_,
>,
> {
Box::pin(line_to(args))
Box::pin(line_to(exec_state, args))
}
impl crate::docs::StdLibFn for LineTo {

View File

@ -77,15 +77,17 @@ pub(crate) struct Min {}
#[doc = "Std lib function: min\nThis is some function.\nIt does shit."]
pub(crate) const Min: Min = Min {};
fn boxed_min(
exec_state: &mut crate::executor::ExecState,
args: crate::std::Args,
) -> std::pin::Pin<
Box<
dyn std::future::Future<
Output = anyhow::Result<crate::executor::KclValue, crate::errors::KclError>,
> + Send,
> + Send
+ '_,
>,
> {
Box::pin(min(args))
Box::pin(min(exec_state, args))
}
impl crate::docs::StdLibFn for Min {

View File

@ -44,15 +44,17 @@ pub(crate) struct Show {}
#[doc = "Std lib function: show\nThis is some function.\nIt does shit."]
pub(crate) const Show: Show = Show {};
fn boxed_show(
exec_state: &mut crate::executor::ExecState,
args: crate::std::Args,
) -> std::pin::Pin<
Box<
dyn std::future::Future<
Output = anyhow::Result<crate::executor::KclValue, crate::errors::KclError>,
> + Send,
> + Send
+ '_,
>,
> {
Box::pin(show(args))
Box::pin(show(exec_state, args))
}
impl crate::docs::StdLibFn for Show {

View File

@ -44,15 +44,17 @@ pub(crate) struct Import {}
#[doc = "Std lib function: import\nThis is some function.\nIt does shit."]
pub(crate) const Import: Import = Import {};
fn boxed_import(
exec_state: &mut crate::executor::ExecState,
args: crate::std::Args,
) -> std::pin::Pin<
Box<
dyn std::future::Future<
Output = anyhow::Result<crate::executor::KclValue, crate::errors::KclError>,
> + Send,
> + Send
+ '_,
>,
> {
Box::pin(import(args))
Box::pin(import(exec_state, args))
}
impl crate::docs::StdLibFn for Import {

View File

@ -44,15 +44,17 @@ pub(crate) struct Import {}
#[doc = "Std lib function: import\nThis is some function.\nIt does shit."]
pub(crate) const Import: Import = Import {};
fn boxed_import(
exec_state: &mut crate::executor::ExecState,
args: crate::std::Args,
) -> std::pin::Pin<
Box<
dyn std::future::Future<
Output = anyhow::Result<crate::executor::KclValue, crate::errors::KclError>,
> + Send,
> + Send
+ '_,
>,
> {
Box::pin(import(args))
Box::pin(import(exec_state, args))
}
impl crate::docs::StdLibFn for Import {

View File

@ -44,15 +44,17 @@ pub(crate) struct Import {}
#[doc = "Std lib function: import\nThis is some function.\nIt does shit."]
pub(crate) const Import: Import = Import {};
fn boxed_import(
exec_state: &mut crate::executor::ExecState,
args: crate::std::Args,
) -> std::pin::Pin<
Box<
dyn std::future::Future<
Output = anyhow::Result<crate::executor::KclValue, crate::errors::KclError>,
> + Send,
> + Send
+ '_,
>,
> {
Box::pin(import(args))
Box::pin(import(exec_state, args))
}
impl crate::docs::StdLibFn for Import {

View File

@ -44,15 +44,17 @@ pub(crate) struct Show {}
#[doc = "Std lib function: show\nThis is some function.\nIt does shit."]
pub(crate) const Show: Show = Show {};
fn boxed_show(
exec_state: &mut crate::executor::ExecState,
args: crate::std::Args,
) -> std::pin::Pin<
Box<
dyn std::future::Future<
Output = anyhow::Result<crate::executor::KclValue, crate::errors::KclError>,
> + Send,
> + Send
+ '_,
>,
> {
Box::pin(show(args))
Box::pin(show(exec_state, args))
}
impl crate::docs::StdLibFn for Show {

View File

@ -0,0 +1,134 @@
#[cfg(test)]
mod test_examples_some_function {
#[tokio::test(flavor = "multi_thread")]
async fn test_mock_example_some_function0() {
let tokens = crate::token::lexer("someFunction()").unwrap();
let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap();
let ctx = crate::executor::ExecutorContext {
engine: std::sync::Arc::new(Box::new(
crate::engine::conn_mock::EngineConnection::new()
.await
.unwrap(),
)),
fs: std::sync::Arc::new(crate::fs::FileManager::new()),
stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
settings: Default::default(),
is_mock: true,
};
ctx.run(&program, None).await.unwrap();
}
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
async fn kcl_test_example_some_function0() {
let code = "someFunction()";
let result =
crate::test_server::execute_and_snapshot(code, crate::settings::types::UnitLength::Mm)
.await
.unwrap();
twenty_twenty::assert_image(
&format!("tests/outputs/{}.png", "serial_test_example_some_function0"),
&result,
0.99,
);
}
}
#[allow(non_camel_case_types, missing_docs)]
#[doc = "Std lib function: someFunction\nDocs"]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, schemars :: JsonSchema, ts_rs :: TS)]
#[ts(export)]
pub(crate) struct SomeFunction {}
#[allow(non_upper_case_globals, missing_docs)]
#[doc = "Std lib function: someFunction\nDocs"]
pub(crate) const SomeFunction: SomeFunction = SomeFunction {};
fn boxed_some_function(
exec_state: &mut crate::executor::ExecState,
args: crate::std::Args,
) -> std::pin::Pin<
Box<
dyn std::future::Future<
Output = anyhow::Result<crate::executor::KclValue, crate::errors::KclError>,
> + Send
+ '_,
>,
> {
Box::pin(some_function(exec_state, args))
}
impl crate::docs::StdLibFn for SomeFunction {
fn name(&self) -> String {
"someFunction".to_string()
}
fn summary(&self) -> String {
"Docs".to_string()
}
fn description(&self) -> String {
"".to_string()
}
fn tags(&self) -> Vec<String> {
vec![]
}
fn args(&self) -> Vec<crate::docs::StdLibFnArg> {
let mut settings = schemars::gen::SchemaSettings::openapi3();
settings.inline_subschemas = true;
let mut generator = schemars::gen::SchemaGenerator::new(settings);
vec![]
}
fn return_value(&self) -> Option<crate::docs::StdLibFnArg> {
let mut settings = schemars::gen::SchemaSettings::openapi3();
settings.inline_subschemas = true;
let mut generator = schemars::gen::SchemaGenerator::new(settings);
Some(crate::docs::StdLibFnArg {
name: "".to_string(),
type_: "i32".to_string(),
schema: <i32>::json_schema(&mut generator),
required: true,
})
}
fn unpublished(&self) -> bool {
false
}
fn deprecated(&self) -> bool {
false
}
fn examples(&self) -> Vec<String> {
let code_blocks = vec!["someFunction()"];
code_blocks
.iter()
.map(|cb| {
let tokens = crate::token::lexer(cb).unwrap();
let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap();
let mut options: crate::ast::types::FormatOptions = Default::default();
options.insert_final_newline = false;
program.recast(&options, 0)
})
.collect::<Vec<String>>()
}
fn std_lib_fn(&self) -> crate::std::StdFn {
boxed_some_function
}
fn clone_box(&self) -> Box<dyn crate::docs::StdLibFn> {
Box::new(self.clone())
}
}
#[doc = r" Docs"]
#[doc = r" ```"]
#[doc = r" someFunction()"]
#[doc = r" ```"]
fn inner_some_function<'a>(exec_state: &mut ExecState, args: &Args) -> i32 {
3
}

View File

@ -7,6 +7,7 @@ use std::{
};
use anyhow::Result;
use async_recursion::async_recursion;
use databake::*;
use parse_display::{Display, FromStr};
use schemars::JsonSchema;
@ -22,8 +23,8 @@ use crate::{
docs::StdLibFn,
errors::{KclError, KclErrorDetails},
executor::{
BodyType, DynamicState, ExecutorContext, KclValue, Metadata, PipeInfo, ProgramMemory, SketchGroup, SourceRange,
StatementKind, TagEngineInfo, TagIdentifier, UserVal,
BodyType, ExecState, ExecutorContext, KclValue, Metadata, SketchGroup, SourceRange, StatementKind,
TagEngineInfo, TagIdentifier, UserVal,
},
parser::PIPE_OPERATOR,
std::{kcl_stdlib::KclStdLibFn, FunctionKind},
@ -797,31 +798,17 @@ impl BinaryPart {
}
#[async_recursion::async_recursion]
pub async fn get_result(
&self,
memory: &mut ProgramMemory,
dynamic_state: &DynamicState,
pipe_info: &PipeInfo,
ctx: &ExecutorContext,
) -> Result<KclValue, KclError> {
pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
match self {
BinaryPart::Literal(literal) => Ok(literal.into()),
BinaryPart::Identifier(identifier) => {
let value = memory.get(&identifier.name, identifier.into())?;
let value = exec_state.memory.get(&identifier.name, identifier.into())?;
Ok(value.clone())
}
BinaryPart::BinaryExpression(binary_expression) => {
binary_expression
.get_result(memory, dynamic_state, pipe_info, ctx)
.await
}
BinaryPart::CallExpression(call_expression) => {
call_expression.execute(memory, dynamic_state, pipe_info, ctx).await
}
BinaryPart::UnaryExpression(unary_expression) => {
unary_expression.get_result(memory, dynamic_state, pipe_info, ctx).await
}
BinaryPart::MemberExpression(member_expression) => member_expression.get_result(memory),
BinaryPart::BinaryExpression(binary_expression) => binary_expression.get_result(exec_state, ctx).await,
BinaryPart::CallExpression(call_expression) => call_expression.execute(exec_state, ctx).await,
BinaryPart::UnaryExpression(unary_expression) => unary_expression.get_result(exec_state, ctx).await,
BinaryPart::MemberExpression(member_expression) => member_expression.get_result(exec_state),
}
}
@ -1192,30 +1179,17 @@ impl CallExpression {
}
#[async_recursion::async_recursion]
pub async fn execute(
&self,
memory: &mut ProgramMemory,
dynamic_state: &DynamicState,
pipe_info: &PipeInfo,
ctx: &ExecutorContext,
) -> Result<KclValue, KclError> {
let fn_name = self.callee.name.clone();
pub async fn execute(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
let fn_name = &self.callee.name;
let mut fn_args: Vec<KclValue> = Vec::with_capacity(self.arguments.len());
for arg in &self.arguments {
let metadata = Metadata {
source_range: SourceRange([arg.start(), arg.end()]),
source_range: SourceRange::from(arg),
};
let result = ctx
.execute_expr(
arg,
memory,
dynamic_state,
pipe_info,
&metadata,
StatementKind::Expression,
)
.execute_expr(arg, exec_state, &metadata, StatementKind::Expression)
.await?;
fn_args.push(result);
}
@ -1223,9 +1197,8 @@ impl CallExpression {
match ctx.stdlib.get_either(&self.callee.name) {
FunctionKind::Core(func) => {
// Attempt to call the function.
let args =
crate::std::Args::new(fn_args, self.into(), ctx.clone(), memory.clone(), dynamic_state.clone());
let mut result = func.std_lib_fn()(args).await?;
let args = crate::std::Args::new(fn_args, self.into(), ctx.clone());
let mut result = func.std_lib_fn()(exec_state, args).await?;
// If the return result is a sketch group or extrude group, we want to update the
// memory for the tags of the group.
@ -1235,7 +1208,7 @@ impl CallExpression {
KclValue::UserVal(ref mut uval) => {
uval.mutate(|sketch_group: &mut SketchGroup| {
for (_, tag) in sketch_group.tags.iter() {
memory.update_tag(&tag.value, tag.clone())?;
exec_state.memory.update_tag(&tag.value, tag.clone())?;
}
Ok::<_, KclError>(())
})?;
@ -1275,7 +1248,7 @@ impl CallExpression {
info.sketch_group = extrude_group.id;
t.info = Some(info);
memory.update_tag(&tag.name, t.clone())?;
exec_state.memory.update_tag(&tag.name, t.clone())?;
// update the sketch group tags.
extrude_group.sketch_group.tags.insert(tag.name.clone(), t);
@ -1283,7 +1256,11 @@ impl CallExpression {
}
// Find the stale sketch group in memory and update it.
if let Some(current_env) = memory.environments.get_mut(memory.current_env.index()) {
if let Some(current_env) = exec_state
.memory
.environments
.get_mut(exec_state.memory.current_env.index())
{
current_env.update_sketch_group_tags(&extrude_group.sketch_group);
}
}
@ -1294,17 +1271,18 @@ impl CallExpression {
}
FunctionKind::Std(func) => {
let function_expression = func.function();
let parts = function_expression.clone().into_parts().map_err(|e| {
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() < parts.params_required.len() || fn_args.len() > function_expression.params.len() {
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(
&param.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(&param.identifier.name, arg.clone(), param.identifier.clone().into())?;
} else {
fn_memory.add(
@ -1336,22 +1314,31 @@ impl CallExpression {
}
}
let mut fn_dynamic_state = dynamic_state.clone();
let fn_dynamic_state = exec_state.dynamic_state.clone();
// TODO: Shouldn't we merge program memory into fn_dynamic_state
// here?
// Call the stdlib function
let p = func.function().clone().body;
let results = match ctx
.inner_execute(&p, &mut fn_memory, &mut fn_dynamic_state, BodyType::Block)
.await
{
Ok(results) => results,
let p = &func.function().body;
let (exec_result, fn_memory) = {
let previous_memory = std::mem::replace(&mut exec_state.memory, fn_memory);
let previous_dynamic_state = std::mem::replace(&mut exec_state.dynamic_state, fn_dynamic_state);
let result = ctx.inner_execute(p, exec_state, BodyType::Block).await;
exec_state.dynamic_state = previous_dynamic_state;
let fn_memory = std::mem::replace(&mut exec_state.memory, previous_memory);
(result, fn_memory)
};
match exec_result {
Ok(()) => {}
Err(err) => {
// We need to override the source ranges so we don't get the embedded kcl
// function from the stdlib.
return Err(err.override_source_ranges(vec![self.into()]));
}
};
let out = results.return_;
let out = fn_memory.return_;
let result = out.ok_or_else(|| {
KclError::UndefinedValue(KclErrorDetails {
message: format!("Result of stdlib function {} is undefined", fn_name),
@ -1361,18 +1348,24 @@ impl CallExpression {
Ok(result)
}
FunctionKind::UserDefined => {
let func = memory.get(&fn_name, self.into())?;
let fn_dynamic_state = dynamic_state.merge(memory);
let result = func
.call_fn(fn_args, &fn_dynamic_state, ctx.clone())
.await
.map_err(|e| {
// Add the call expression to the source ranges.
e.add_source_ranges(vec![self.into()])
})?;
let source_range = SourceRange::from(self);
// Clone the function so that we can use a mutable reference to
// exec_state.
let func = exec_state.memory.get(fn_name, source_range)?.clone();
let fn_dynamic_state = exec_state.dynamic_state.merge(&exec_state.memory);
let result = result.ok_or_else(|| {
let mut source_ranges: Vec<SourceRange> = vec![self.into()];
let return_value = {
let previous_dynamic_state = std::mem::replace(&mut exec_state.dynamic_state, fn_dynamic_state);
let result = func.call_fn(fn_args, exec_state, ctx.clone()).await.map_err(|e| {
// Add the call expression to the source ranges.
e.add_source_ranges(vec![source_range])
});
exec_state.dynamic_state = previous_dynamic_state;
result?
};
let result = return_value.ok_or_else(move || {
let mut source_ranges: Vec<SourceRange> = vec![source_range];
// We want to send the source range of the original function.
if let KclValue::Function { meta, .. } = func {
source_ranges = meta.iter().map(|m| m.source_range).collect();
@ -1990,7 +1983,7 @@ impl TagDeclarator {
}
}
pub async fn execute(&self, memory: &mut ProgramMemory) -> Result<KclValue, KclError> {
pub async fn execute(&self, exec_state: &mut ExecState) -> Result<KclValue, KclError> {
let memory_item = KclValue::TagIdentifier(Box::new(TagIdentifier {
value: self.name.clone(),
info: None,
@ -1999,7 +1992,7 @@ impl TagDeclarator {
}],
}));
memory.add(&self.name, memory_item.clone(), self.into())?;
exec_state.memory.add(&self.name, memory_item.clone(), self.into())?;
Ok(self.into())
}
@ -2136,65 +2129,18 @@ impl ArrayExpression {
}
#[async_recursion::async_recursion]
pub async fn execute(
&self,
memory: &mut ProgramMemory,
dynamic_state: &DynamicState,
pipe_info: &PipeInfo,
ctx: &ExecutorContext,
) -> Result<KclValue, KclError> {
pub async fn execute(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
let mut results = Vec::with_capacity(self.elements.len());
for element in &self.elements {
let result = match element {
Expr::Literal(literal) => literal.into(),
Expr::TagDeclarator(tag) => tag.execute(memory).await?,
Expr::None(none) => none.into(),
Expr::Identifier(identifier) => {
let value = memory.get(&identifier.name, identifier.into())?;
value.clone()
}
Expr::BinaryExpression(binary_expression) => {
binary_expression
.get_result(memory, dynamic_state, pipe_info, ctx)
.await?
}
Expr::CallExpression(call_expression) => {
call_expression.execute(memory, dynamic_state, pipe_info, ctx).await?
}
Expr::UnaryExpression(unary_expression) => {
unary_expression
.get_result(memory, dynamic_state, pipe_info, ctx)
.await?
}
Expr::ObjectExpression(object_expression) => {
object_expression.execute(memory, dynamic_state, pipe_info, ctx).await?
}
Expr::ArrayExpression(array_expression) => {
array_expression.execute(memory, dynamic_state, pipe_info, ctx).await?
}
Expr::PipeExpression(pipe_expression) => {
pipe_expression
.get_result(memory, dynamic_state, pipe_info, ctx)
.await?
}
Expr::PipeSubstitution(pipe_substitution) => {
return Err(KclError::Semantic(KclErrorDetails {
message: format!("PipeSubstitution not implemented here: {:?}", pipe_substitution),
source_ranges: vec![pipe_substitution.into()],
}));
}
Expr::MemberExpression(member_expression) => member_expression.get_result(memory)?,
Expr::FunctionExpression(function_expression) => {
return Err(KclError::Semantic(KclErrorDetails {
message: format!("FunctionExpression not implemented here: {:?}", function_expression),
source_ranges: vec![function_expression.into()],
}));
}
}
.get_json_value()?;
let metadata = Metadata::from(element);
// TODO: Carry statement kind here so that we know if we're
// inside a variable declaration.
let value = ctx
.execute_expr(element, exec_state, &metadata, StatementKind::Expression)
.await?;
results.push(result);
results.push(value.get_json_value()?);
}
Ok(KclValue::UserVal(UserVal {
@ -2279,61 +2225,13 @@ impl ObjectExpression {
}
#[async_recursion::async_recursion]
pub async fn execute(
&self,
memory: &mut ProgramMemory,
dynamic_state: &DynamicState,
pipe_info: &PipeInfo,
ctx: &ExecutorContext,
) -> Result<KclValue, KclError> {
pub async fn execute(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
let mut object = Map::new();
for property in &self.properties {
let result = match &property.value {
Expr::Literal(literal) => literal.into(),
Expr::TagDeclarator(tag) => tag.execute(memory).await?,
Expr::None(none) => none.into(),
Expr::Identifier(identifier) => {
let value = memory.get(&identifier.name, identifier.into())?;
value.clone()
}
Expr::BinaryExpression(binary_expression) => {
binary_expression
.get_result(memory, dynamic_state, pipe_info, ctx)
.await?
}
Expr::CallExpression(call_expression) => {
call_expression.execute(memory, dynamic_state, pipe_info, ctx).await?
}
Expr::UnaryExpression(unary_expression) => {
unary_expression
.get_result(memory, dynamic_state, pipe_info, ctx)
.await?
}
Expr::ObjectExpression(object_expression) => {
object_expression.execute(memory, dynamic_state, pipe_info, ctx).await?
}
Expr::ArrayExpression(array_expression) => {
array_expression.execute(memory, dynamic_state, pipe_info, ctx).await?
}
Expr::PipeExpression(pipe_expression) => {
pipe_expression
.get_result(memory, dynamic_state, pipe_info, ctx)
.await?
}
Expr::MemberExpression(member_expression) => member_expression.get_result(memory)?,
Expr::PipeSubstitution(pipe_substitution) => {
return Err(KclError::Semantic(KclErrorDetails {
message: format!("PipeSubstitution not implemented here: {:?}", pipe_substitution),
source_ranges: vec![pipe_substitution.into()],
}));
}
Expr::FunctionExpression(function_expression) => {
return Err(KclError::Semantic(KclErrorDetails {
message: format!("FunctionExpression not implemented here: {:?}", function_expression),
source_ranges: vec![function_expression.into()],
}));
}
};
let metadata = Metadata::from(&property.value);
let result = ctx
.execute_expr(&property.value, exec_state, &metadata, StatementKind::Expression)
.await?;
object.insert(property.key.name.clone(), result.get_json_value()?);
}
@ -2545,11 +2443,11 @@ impl MemberExpression {
None
}
pub fn get_result_array(&self, memory: &mut ProgramMemory, index: usize) -> Result<KclValue, KclError> {
pub fn get_result_array(&self, exec_state: &mut ExecState, index: usize) -> Result<KclValue, KclError> {
let array = match &self.object {
MemberObject::MemberExpression(member_expr) => member_expr.get_result(memory)?,
MemberObject::MemberExpression(member_expr) => member_expr.get_result(exec_state)?,
MemberObject::Identifier(identifier) => {
let value = memory.get(&identifier.name, identifier.into())?;
let value = exec_state.memory.get(&identifier.name, identifier.into())?;
value.clone()
}
};
@ -2578,7 +2476,7 @@ impl MemberExpression {
}
}
pub fn get_result(&self, memory: &mut ProgramMemory) -> Result<KclValue, KclError> {
pub fn get_result(&self, exec_state: &mut ExecState) -> Result<KclValue, KclError> {
#[derive(Debug)]
enum Property {
Number(usize),
@ -2605,7 +2503,7 @@ impl MemberExpression {
Property::String(name.to_string())
} else {
// Actually evaluate memory to compute the property.
let prop = memory.get(&name, property_src)?;
let prop = exec_state.memory.get(&name, property_src)?;
let KclValue::UserVal(prop) = prop else {
return Err(KclError::Semantic(KclErrorDetails {
source_ranges: property_sr,
@ -2667,9 +2565,9 @@ impl MemberExpression {
let object = match &self.object {
// TODO: Don't use recursion here, use a loop.
MemberObject::MemberExpression(member_expr) => member_expr.get_result(memory)?,
MemberObject::MemberExpression(member_expr) => member_expr.get_result(exec_state)?,
MemberObject::Identifier(identifier) => {
let value = memory.get(&identifier.name, identifier.into())?;
let value = exec_state.memory.get(&identifier.name, identifier.into())?;
value.clone()
}
};
@ -2827,23 +2725,9 @@ impl BinaryExpression {
}
#[async_recursion::async_recursion]
pub async fn get_result(
&self,
memory: &mut ProgramMemory,
dynamic_state: &DynamicState,
pipe_info: &PipeInfo,
ctx: &ExecutorContext,
) -> Result<KclValue, KclError> {
let left_json_value = self
.left
.get_result(memory, dynamic_state, pipe_info, ctx)
.await?
.get_json_value()?;
let right_json_value = self
.right
.get_result(memory, dynamic_state, pipe_info, ctx)
.await?
.get_json_value()?;
pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
let left_json_value = self.left.get_result(exec_state, ctx).await?.get_json_value()?;
let right_json_value = self.right.get_result(exec_state, ctx).await?.get_json_value()?;
// First check if we are doing string concatenation.
if self.operator == BinaryOperator::Add {
@ -3043,19 +2927,9 @@ impl UnaryExpression {
self.argument.get_constraint_level()
}
pub async fn get_result(
&self,
memory: &mut ProgramMemory,
dynamic_state: &DynamicState,
pipe_info: &PipeInfo,
ctx: &ExecutorContext,
) -> Result<KclValue, KclError> {
pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
if self.operator == UnaryOperator::Not {
let value = self
.argument
.get_result(memory, dynamic_state, pipe_info, ctx)
.await?
.get_json_value()?;
let value = self.argument.get_result(exec_state, ctx).await?.get_json_value()?;
let Some(bool_value) = json_as_bool(&value) else {
return Err(KclError::Semantic(KclErrorDetails {
message: format!("Cannot apply unary operator ! to non-boolean value: {}", value),
@ -3072,11 +2946,7 @@ impl UnaryExpression {
}
let num = parse_json_number_as_f64(
&self
.argument
.get_result(memory, dynamic_state, pipe_info, ctx)
.await?
.get_json_value()?,
&self.argument.get_result(exec_state, ctx).await?.get_json_value()?,
self.into(),
)?;
Ok(KclValue::UserVal(UserVal {
@ -3204,14 +3074,9 @@ impl PipeExpression {
None
}
pub async fn get_result(
&self,
memory: &mut ProgramMemory,
dynamic_state: &DynamicState,
pipe_info: &PipeInfo,
ctx: &ExecutorContext,
) -> Result<KclValue, KclError> {
execute_pipe_body(memory, dynamic_state, &self.body, pipe_info, self.into(), ctx).await
#[async_recursion]
pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
execute_pipe_body(exec_state, &self.body, self.into(), ctx).await
}
/// Rename all identifiers that have the old name to the new given name.
@ -3222,45 +3087,49 @@ impl PipeExpression {
}
}
#[async_recursion::async_recursion]
async fn execute_pipe_body(
memory: &mut ProgramMemory,
dynamic_state: &DynamicState,
exec_state: &mut ExecState,
body: &[Expr],
pipe_info: &PipeInfo,
source_range: SourceRange,
ctx: &ExecutorContext,
) -> Result<KclValue, KclError> {
let mut body = body.iter();
let first = body.next().ok_or_else(|| {
KclError::Semantic(KclErrorDetails {
let Some((first, body)) = body.split_first() else {
return Err(KclError::Semantic(KclErrorDetails {
message: "Pipe expressions cannot be empty".to_owned(),
source_ranges: vec![source_range],
})
})?;
}));
};
// Evaluate the first element in the pipeline.
// They use the `pipe_info` from some AST node above this, so that if pipe expression is nested in a larger pipe expression,
// They use the pipe_value from some AST node above this, so that if pipe expression is nested in a larger pipe expression,
// they use the % from the parent. After all, this pipe expression hasn't been executed yet, so it doesn't have any % value
// of its own.
let meta = Metadata {
source_range: SourceRange([first.start(), first.end()]),
};
let output = ctx
.execute_expr(
first,
memory,
dynamic_state,
pipe_info,
&meta,
StatementKind::Expression,
)
.execute_expr(first, exec_state, &meta, StatementKind::Expression)
.await?;
// Now that we've evaluated the first child expression in the pipeline, following child expressions
// should use the previous child expression for %.
// This means there's no more need for the previous `pipe_info` from the parent AST node above this one.
let mut new_pipe_info = PipeInfo::new();
new_pipe_info.previous_results = Some(output);
// This means there's no more need for the previous pipe_value from the parent AST node above this one.
let previous_pipe_value = std::mem::replace(&mut exec_state.pipe_value, Some(output));
// Evaluate remaining elements.
let result = inner_execute_pipe_body(exec_state, body, ctx).await;
// Restore the previous pipe value.
exec_state.pipe_value = previous_pipe_value;
result
}
/// Execute the tail of a pipe expression. exec_state.pipe_value must be set by
/// the caller.
#[async_recursion]
async fn inner_execute_pipe_body(
exec_state: &mut ExecState,
body: &[Expr],
ctx: &ExecutorContext,
) -> Result<KclValue, KclError> {
for expression in body {
match expression {
Expr::TagDeclarator(_) => {
@ -3286,19 +3155,12 @@ async fn execute_pipe_body(
source_range: SourceRange([expression.start(), expression.end()]),
};
let output = ctx
.execute_expr(
expression,
memory,
dynamic_state,
&new_pipe_info,
&metadata,
StatementKind::Expression,
)
.execute_expr(expression, exec_state, &metadata, StatementKind::Expression)
.await?;
new_pipe_info.previous_results = Some(output);
exec_state.pipe_value = Some(output);
}
// Safe to unwrap here, because `newpipe_info` always has something pushed in when the `match first` executes.
let final_output = new_pipe_info.previous_results.unwrap();
// Safe to unwrap here, because pipe_value always has something pushed in when the `match first` executes.
let final_output = exec_state.pipe_value.take().unwrap();
Ok(final_output)
}
@ -3430,14 +3292,6 @@ pub struct FunctionExpression {
impl_value_meta!(FunctionExpression);
pub struct FunctionExpressionParts {
pub start: usize,
pub end: usize,
pub params_required: Vec<Parameter>,
pub params_optional: Vec<Parameter>,
pub body: Program,
}
#[derive(Debug, PartialEq, Clone)]
pub struct RequiredParamAfterOptionalParam(pub Parameter);
@ -3472,36 +3326,28 @@ impl FunctionExpression {
}
});
pub fn into_parts(self) -> Result<FunctionExpressionParts, RequiredParamAfterOptionalParam> {
pub fn required_and_optional_params(
&self,
) -> Result<(&[Parameter], &[Parameter]), RequiredParamAfterOptionalParam> {
let Self {
start,
end,
start: _,
end: _,
params,
body,
body: _,
digest: _,
return_type: _,
} = self;
let mut params_required = Vec::with_capacity(params.len());
let mut params_optional = Vec::with_capacity(params.len());
let mut found_optional = false;
for param in params {
if param.optional {
params_optional.push(param);
} else {
if !params_optional.is_empty() {
return Err(RequiredParamAfterOptionalParam(param));
}
params_required.push(param);
found_optional = true;
} else if found_optional {
return Err(RequiredParamAfterOptionalParam(param.clone()));
}
}
params_required.shrink_to_fit();
params_optional.shrink_to_fit();
Ok(FunctionExpressionParts {
start,
end,
params_required,
params_optional,
body,
})
let boundary = self.params.partition_point(|param| !param.optional);
// SAFETY: split_at panics if the boundary is greater than the length.
Ok(self.params.split_at(boundary))
}
/// Required parameters must be declared before optional parameters.

View File

@ -23,6 +23,20 @@ use crate::{
std::{FnAsArg, StdLib},
};
/// State for executing a program.
#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)]
#[serde(rename_all = "camelCase")]
pub struct ExecState {
/// Program variable bindings.
pub memory: ProgramMemory,
/// Dynamic state that follows dynamic flow of the program.
pub dynamic_state: DynamicState,
/// The current value of the pipe operator returned from the previous
/// expression. If we're not currently in a pipeline, this will be None.
pub pipe_value: Option<KclValue>,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)]
#[serde(rename_all = "camelCase")]
@ -227,7 +241,7 @@ impl Environment {
/// Dynamic state that depends on the dynamic flow of the program, like the call
/// stack. If the language had exceptions, for example, you could store the
/// stack of exception handlers here.
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, ts_rs::TS, JsonSchema)]
#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize, ts_rs::TS, JsonSchema)]
pub struct DynamicState {
pub extrude_group_ids: Vec<ExtrudeGroupLazyIds>,
}
@ -730,25 +744,10 @@ pub type MemoryFunction =
memory: ProgramMemory,
expression: Box<FunctionExpression>,
metadata: Vec<Metadata>,
dynamic_state: DynamicState,
exec_state: &ExecState,
ctx: ExecutorContext,
) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<Option<KclValue>, KclError>> + Send>>;
fn force_memory_function<
F: Fn(
Vec<KclValue>,
ProgramMemory,
Box<FunctionExpression>,
Vec<Metadata>,
DynamicState,
ExecutorContext,
) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<Option<KclValue>, KclError>> + Send>>,
>(
f: F,
) -> F {
f
}
impl From<KclValue> for Vec<SourceRange> {
fn from(item: KclValue) -> Self {
match item {
@ -848,9 +847,8 @@ impl KclValue {
else {
return None;
};
let func = func.as_ref()?;
Some(FnAsArg {
func,
func: func.as_ref(),
expr: expression.to_owned(),
memory: memory.to_owned(),
})
@ -904,7 +902,7 @@ impl KclValue {
pub async fn call_fn(
&self,
args: Vec<KclValue>,
dynamic_state: &DynamicState,
exec_state: &mut ExecState,
ctx: ExecutorContext,
) -> Result<Option<KclValue>, KclError> {
let KclValue::Function {
@ -919,21 +917,19 @@ impl KclValue {
source_ranges: vec![],
}));
};
let Some(func) = func else {
return Err(KclError::Semantic(KclErrorDetails {
message: format!("Not a function: {:?}", expression),
source_ranges: vec![],
}));
};
if let Some(func) = func {
func(
args,
closure_memory.as_ref().clone(),
expression.clone(),
meta.clone(),
dynamic_state.clone(),
exec_state,
ctx,
)
.await
} else {
call_user_defined_function(args, closure_memory.as_ref(), expression.as_ref(), exec_state, &ctx).await
}
}
}
@ -1356,6 +1352,14 @@ impl From<&ReturnStatement> for Metadata {
}
}
impl From<&Expr> for Metadata {
fn from(expr: &Expr) -> Self {
Self {
source_range: SourceRange::from(expr),
}
}
}
/// A base path.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)]
@ -1573,25 +1577,6 @@ impl ExtrudeSurface {
}
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)]
#[serde(rename_all = "camelCase")]
pub struct PipeInfo {
pub previous_results: Option<KclValue>,
}
impl PipeInfo {
pub fn new() -> Self {
Self { previous_results: None }
}
}
impl Default for PipeInfo {
fn default() -> Self {
Self::new()
}
}
/// The executor context.
/// Cloning will return another handle to the same engine connection/session,
/// as this uses `Arc` under the hood.
@ -1773,7 +1758,7 @@ impl ExecutorContext {
&self,
program: &crate::ast::types::Program,
memory: Option<ProgramMemory>,
) -> Result<ProgramMemory, KclError> {
) -> Result<ExecState, KclError> {
self.run_with_session_data(program, memory).await.map(|x| x.0)
}
/// Perform the execution of a program.
@ -1783,7 +1768,7 @@ impl ExecutorContext {
&self,
program: &crate::ast::types::Program,
memory: Option<ProgramMemory>,
) -> Result<(ProgramMemory, Option<ModelingSessionData>), KclError> {
) -> Result<(ExecState, Option<ModelingSessionData>), KclError> {
// Before we even start executing the program, set the units.
self.engine
.batch_modeling_cmd(
@ -1794,22 +1779,19 @@ impl ExecutorContext {
},
)
.await?;
let mut memory = if let Some(memory) = memory {
let memory = if let Some(memory) = memory {
memory.clone()
} else {
Default::default()
};
let mut dynamic_state = DynamicState::default();
let final_memory = self
.inner_execute(
program,
&mut memory,
&mut dynamic_state,
crate::executor::BodyType::Root,
)
let mut exec_state = ExecState {
memory,
..Default::default()
};
self.inner_execute(program, &mut exec_state, crate::executor::BodyType::Root)
.await?;
let session_data = self.engine.get_session_data();
Ok((final_memory, session_data))
Ok((exec_state, session_data))
}
/// Execute an AST's program.
@ -1817,12 +1799,9 @@ impl ExecutorContext {
pub(crate) async fn inner_execute(
&self,
program: &crate::ast::types::Program,
memory: &mut ProgramMemory,
dynamic_state: &mut DynamicState,
exec_state: &mut ExecState,
body_type: BodyType,
) -> Result<ProgramMemory, KclError> {
let pipe_info = PipeInfo::default();
) -> Result<(), KclError> {
// Iterate over the body of the program.
for statement in &program.body {
match statement {
@ -1831,9 +1810,7 @@ impl ExecutorContext {
// Discard return value.
self.execute_expr(
&expression_statement.expression,
memory,
dynamic_state,
&pipe_info,
exec_state,
&metadata,
StatementKind::Expression,
)
@ -1848,14 +1825,12 @@ impl ExecutorContext {
let memory_item = self
.execute_expr(
&declaration.init,
memory,
dynamic_state,
&pipe_info,
exec_state,
&metadata,
StatementKind::Declaration { name: &var_name },
)
.await?;
memory.add(&var_name, memory_item, source_range)?;
exec_state.memory.add(&var_name, memory_item, source_range)?;
}
}
BodyItem::ReturnStatement(return_statement) => {
@ -1863,14 +1838,12 @@ impl ExecutorContext {
let value = self
.execute_expr(
&return_statement.argument,
memory,
dynamic_state,
&pipe_info,
exec_state,
&metadata,
StatementKind::Expression,
)
.await?;
memory.return_ = Some(value);
exec_state.memory.return_ = Some(value);
}
}
}
@ -1887,81 +1860,38 @@ impl ExecutorContext {
.await?;
}
Ok(memory.clone())
Ok(())
}
pub async fn execute_expr<'a>(
&self,
init: &Expr,
memory: &mut ProgramMemory,
dynamic_state: &DynamicState,
pipe_info: &PipeInfo,
exec_state: &mut ExecState,
metadata: &Metadata,
statement_kind: StatementKind<'a>,
) -> Result<KclValue, KclError> {
let item = match init {
Expr::None(none) => KclValue::from(none),
Expr::Literal(literal) => KclValue::from(literal),
Expr::TagDeclarator(tag) => tag.execute(memory).await?,
Expr::TagDeclarator(tag) => tag.execute(exec_state).await?,
Expr::Identifier(identifier) => {
let value = memory.get(&identifier.name, identifier.into())?;
let value = exec_state.memory.get(&identifier.name, identifier.into())?;
value.clone()
}
Expr::BinaryExpression(binary_expression) => {
binary_expression
.get_result(memory, dynamic_state, pipe_info, self)
.await?
}
Expr::BinaryExpression(binary_expression) => binary_expression.get_result(exec_state, self).await?,
Expr::FunctionExpression(function_expression) => {
let mem_func = force_memory_function(
|args: Vec<KclValue>,
memory: ProgramMemory,
function_expression: Box<FunctionExpression>,
_metadata: Vec<Metadata>,
mut dynamic_state: DynamicState,
ctx: ExecutorContext| {
Box::pin(async move {
// Create a new environment to execute the function
// body in so that local variables shadow variables
// in the parent scope. The new environment's
// parent should be the environment of the closure.
let mut body_memory = memory.clone();
let closure_env = memory.current_env;
let body_env = body_memory.new_env_for_call(closure_env);
body_memory.current_env = body_env;
let mut fn_memory = assign_args_to_params(&function_expression, args, body_memory)?;
let result = ctx
.inner_execute(
&function_expression.body,
&mut fn_memory,
&mut dynamic_state,
BodyType::Block,
)
.await?;
Ok(result.return_)
})
},
);
// Cloning memory here is crucial for semantics so that we close
// over variables. Variables defined lexically later shouldn't
// be available to the function body.
KclValue::Function {
expression: function_expression.clone(),
meta: vec![metadata.to_owned()],
func: Some(mem_func),
memory: Box::new(memory.clone()),
func: None,
memory: Box::new(exec_state.memory.clone()),
}
}
Expr::CallExpression(call_expression) => {
call_expression.execute(memory, dynamic_state, pipe_info, self).await?
}
Expr::PipeExpression(pipe_expression) => {
pipe_expression
.get_result(memory, dynamic_state, pipe_info, self)
.await?
}
Expr::CallExpression(call_expression) => call_expression.execute(exec_state, self).await?,
Expr::PipeExpression(pipe_expression) => pipe_expression.get_result(exec_state, self).await?,
Expr::PipeSubstitution(pipe_substitution) => match statement_kind {
StatementKind::Declaration { name } => {
let message = format!(
@ -1973,7 +1903,7 @@ impl ExecutorContext {
source_ranges: vec![pipe_substitution.into()],
}));
}
StatementKind::Expression => match pipe_info.previous_results.clone() {
StatementKind::Expression => match exec_state.pipe_value.clone() {
Some(x) => x,
None => {
return Err(KclError::Semantic(KclErrorDetails {
@ -1983,20 +1913,10 @@ impl ExecutorContext {
}
},
},
Expr::ArrayExpression(array_expression) => {
array_expression.execute(memory, dynamic_state, pipe_info, self).await?
}
Expr::ObjectExpression(object_expression) => {
object_expression
.execute(memory, dynamic_state, pipe_info, self)
.await?
}
Expr::MemberExpression(member_expression) => member_expression.get_result(memory)?,
Expr::UnaryExpression(unary_expression) => {
unary_expression
.get_result(memory, dynamic_state, pipe_info, self)
.await?
}
Expr::ArrayExpression(array_expression) => array_expression.execute(exec_state, self).await?,
Expr::ObjectExpression(object_expression) => object_expression.execute(exec_state, self).await?,
Expr::MemberExpression(member_expression) => member_expression.get_result(exec_state)?,
Expr::UnaryExpression(unary_expression) => unary_expression.get_result(exec_state, self).await?,
};
Ok(item)
}
@ -2097,6 +2017,36 @@ fn assign_args_to_params(
Ok(fn_memory)
}
pub(crate) async fn call_user_defined_function(
args: Vec<KclValue>,
memory: &ProgramMemory,
function_expression: &FunctionExpression,
exec_state: &mut ExecState,
ctx: &ExecutorContext,
) -> Result<Option<KclValue>, KclError> {
// Create a new environment to execute the function body in so that local
// variables shadow variables in the parent scope. The new environment's
// parent should be the environment of the closure.
let mut body_memory = memory.clone();
let body_env = body_memory.new_env_for_call(memory.current_env);
body_memory.current_env = body_env;
let fn_memory = assign_args_to_params(function_expression, args, body_memory)?;
// Execute the function body using the memory we just created.
let (result, fn_memory) = {
let previous_memory = std::mem::replace(&mut exec_state.memory, fn_memory);
let result = ctx
.inner_execute(&function_expression.body, exec_state, BodyType::Block)
.await;
// Restore the previous memory.
let fn_memory = std::mem::replace(&mut exec_state.memory, previous_memory);
(result, fn_memory)
};
result.map(|()| fn_memory.return_)
}
pub enum StatementKind<'a> {
Declaration { name: &'a str },
Expression,
@ -2122,9 +2072,9 @@ mod tests {
settings: Default::default(),
is_mock: true,
};
let memory = ctx.run(&program, None).await?;
let exec_state = ctx.run(&program, None).await?;
Ok(memory)
Ok(exec_state.memory)
}
/// Convenience function to get a JSON value from memory and unwrap.

View File

@ -3,30 +3,36 @@ use schemars::JsonSchema;
use crate::{
ast::types::FunctionExpression,
errors::KclError,
executor::{DynamicState, ExecutorContext, KclValue, MemoryFunction, Metadata, ProgramMemory},
executor::{
call_user_defined_function, ExecState, ExecutorContext, KclValue, MemoryFunction, Metadata, ProgramMemory,
},
};
/// A function being used as a parameter into a stdlib function.
/// A function being used as a parameter into a stdlib function. This is a
/// closure, plus everything needed to execute it.
pub struct FunctionParam<'a> {
pub inner: &'a MemoryFunction,
pub inner: Option<&'a MemoryFunction>,
pub memory: ProgramMemory,
pub dynamic_state: DynamicState,
pub fn_expr: Box<FunctionExpression>,
pub meta: Vec<Metadata>,
pub ctx: ExecutorContext,
}
impl<'a> FunctionParam<'a> {
pub async fn call(&self, args: Vec<KclValue>) -> Result<Option<KclValue>, KclError> {
(self.inner)(
pub async fn call(&self, exec_state: &mut ExecState, args: Vec<KclValue>) -> Result<Option<KclValue>, KclError> {
if let Some(inner) = self.inner {
inner(
args,
self.memory.clone(),
self.fn_expr.clone(),
self.meta.clone(),
self.dynamic_state.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
}
}
}

View File

@ -591,8 +591,8 @@ impl Backend {
// Clear the scene, before we execute so it's not fugly as shit.
executor_ctx.engine.clear_scene(SourceRange::default()).await?;
let memory = match executor_ctx.run(ast, None).await {
Ok(memory) => memory,
let exec_state = match executor_ctx.run(ast, None).await {
Ok(exec_state) => exec_state,
Err(err) => {
self.memory_map.remove(params.uri.as_str());
self.add_to_diagnostics(params, &[err], false).await;
@ -603,11 +603,12 @@ impl Backend {
}
};
self.memory_map.insert(params.uri.to_string(), memory.clone());
self.memory_map
.insert(params.uri.to_string(), exec_state.memory.clone());
// Send the notification to the client that the memory was updated.
self.client
.send_notification::<custom_notifications::MemoryUpdated>(memory)
.send_notification::<custom_notifications::MemoryUpdated>(exec_state.memory)
.await;
Ok(())

View File

@ -8,8 +8,8 @@ use crate::{
ast::types::{parse_json_number_as_f64, TagDeclarator},
errors::{KclError, KclErrorDetails},
executor::{
DynamicState, ExecutorContext, ExtrudeGroup, ExtrudeGroupSet, ExtrudeSurface, KclValue, Metadata,
ProgramMemory, SketchGroup, SketchGroupSet, SketchSurface, SourceRange, TagIdentifier,
ExecState, ExecutorContext, ExtrudeGroup, ExtrudeGroupSet, ExtrudeSurface, KclValue, Metadata, SketchGroup,
SketchGroupSet, SketchSurface, SourceRange, TagIdentifier,
},
std::{shapes::SketchSurfaceOrGroup, sketch::FaceTag, FnAsArg},
};
@ -19,24 +19,14 @@ pub struct Args {
pub args: Vec<KclValue>,
pub source_range: SourceRange,
pub ctx: ExecutorContext,
pub current_program_memory: ProgramMemory,
pub dynamic_state: DynamicState,
}
impl Args {
pub fn new(
args: Vec<KclValue>,
source_range: SourceRange,
ctx: ExecutorContext,
current_program_memory: ProgramMemory,
dynamic_state: DynamicState,
) -> Self {
pub fn new(args: Vec<KclValue>, source_range: SourceRange, ctx: ExecutorContext) -> Self {
Self {
args,
source_range,
ctx,
current_program_memory,
dynamic_state,
}
}
@ -54,8 +44,6 @@ impl Args {
settings: Default::default(),
is_mock: true,
},
current_program_memory: ProgramMemory::default(),
dynamic_state: DynamicState::default(),
})
}
@ -88,11 +76,12 @@ impl Args {
self.ctx.engine.send_modeling_cmd(id, self.source_range, cmd).await
}
fn get_tag_info_from_memory<'a>(
fn get_tag_info_from_memory<'a, 'e>(
&'a self,
exec_state: &'e mut ExecState,
tag: &'a TagIdentifier,
) -> Result<&'a crate::executor::TagEngineInfo, KclError> {
if let KclValue::TagIdentifier(t) = self.current_program_memory.get(&tag.value, self.source_range)? {
) -> Result<&'e crate::executor::TagEngineInfo, KclError> {
if let KclValue::TagIdentifier(t) = exec_state.memory.get(&tag.value, self.source_range)? {
Ok(t.info.as_ref().ok_or_else(|| {
KclError::Type(KclErrorDetails {
message: format!("Tag `{}` does not have engine info", tag.value),
@ -107,34 +96,43 @@ impl Args {
}
}
pub(crate) fn get_tag_engine_info<'a>(
pub(crate) fn get_tag_engine_info<'a, 'e>(
&'a self,
exec_state: &'e mut ExecState,
tag: &'a TagIdentifier,
) -> Result<&'a crate::executor::TagEngineInfo, KclError> {
) -> Result<&'a crate::executor::TagEngineInfo, KclError>
where
'e: 'a,
{
if let Some(info) = &tag.info {
return Ok(info);
}
self.get_tag_info_from_memory(tag)
self.get_tag_info_from_memory(exec_state, tag)
}
fn get_tag_engine_info_check_surface<'a>(
fn get_tag_engine_info_check_surface<'a, 'e>(
&'a self,
exec_state: &'e mut ExecState,
tag: &'a TagIdentifier,
) -> Result<&'a crate::executor::TagEngineInfo, KclError> {
) -> Result<&'a crate::executor::TagEngineInfo, KclError>
where
'e: 'a,
{
if let Some(info) = &tag.info {
if info.surface.is_some() {
return Ok(info);
}
}
self.get_tag_info_from_memory(tag)
self.get_tag_info_from_memory(exec_state, tag)
}
/// Flush just the fillets and chamfers for this specific ExtrudeGroupSet.
#[allow(clippy::vec_box)]
pub(crate) async fn flush_batch_for_extrude_group_set(
&self,
exec_state: &mut ExecState,
extrude_groups: Vec<Box<ExtrudeGroup>>,
) -> Result<(), KclError> {
// Make sure we don't traverse sketch_groups more than once.
@ -148,12 +146,13 @@ impl Args {
if !traversed_sketch_groups.contains(&sketch_group_id) {
// Find all the extrude groups on the same shared sketch group.
ids.extend(
self.current_program_memory
exec_state
.memory
.find_extrude_groups_on_sketch_group(extrude_group.sketch_group.id)
.iter()
.flat_map(|eg| eg.get_all_edge_cut_ids()),
);
ids.extend(self.dynamic_state.edge_cut_ids_on_sketch_group(sketch_group_id));
ids.extend(exec_state.dynamic_state.edge_cut_ids_on_sketch_group(sketch_group_id));
traversed_sketch_groups.push(sketch_group_id);
}
@ -380,6 +379,7 @@ impl Args {
pub(crate) async fn get_adjacent_face_to_tag(
&self,
exec_state: &mut ExecState,
tag: &TagIdentifier,
must_be_planar: bool,
) -> Result<uuid::Uuid, KclError> {
@ -390,7 +390,7 @@ impl Args {
}));
}
let engine_info = self.get_tag_engine_info_check_surface(tag)?;
let engine_info = self.get_tag_engine_info_check_surface(exec_state, tag)?;
let surface = engine_info.surface.as_ref().ok_or_else(|| {
KclError::Type(KclErrorDetails {

View File

@ -3,14 +3,14 @@ use schemars::JsonSchema;
use crate::{
errors::{KclError, KclErrorDetails},
executor::{KclValue, SketchGroup, SourceRange, UserVal},
executor::{ExecState, KclValue, SketchGroup, SourceRange, UserVal},
function_param::FunctionParam,
};
use super::{args::FromArgs, Args, FnAsArg};
/// For each item in an array, update a value.
pub async fn array_reduce(args: Args) -> Result<KclValue, KclError> {
pub async fn array_reduce(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (array, start, f): (Vec<u64>, SketchGroup, FnAsArg<'_>) = FromArgs::from_args(&args, 0)?;
let reduce_fn = FunctionParam {
inner: f.func,
@ -18,9 +18,8 @@ pub async fn array_reduce(args: Args) -> Result<KclValue, KclError> {
meta: vec![args.source_range.into()],
ctx: args.ctx.clone(),
memory: *f.memory,
dynamic_state: args.dynamic_state.clone(),
};
inner_array_reduce(array, start, reduce_fn, &args)
inner_array_reduce(array, start, reduce_fn, exec_state, &args)
.await
.map(|sg| KclValue::UserVal(UserVal::set(sg.meta.clone(), sg)))
}
@ -46,11 +45,12 @@ async fn inner_array_reduce<'a>(
array: Vec<u64>,
start: SketchGroup,
reduce_fn: FunctionParam<'a>,
exec_state: &mut ExecState,
args: &'a Args,
) -> Result<SketchGroup, KclError> {
let mut reduced = start;
for i in array {
reduced = call_reduce_closure(i, reduced, &reduce_fn, args.source_range).await?;
reduced = call_reduce_closure(i, reduced, &reduce_fn, args.source_range, exec_state).await?;
}
Ok(reduced)
@ -61,6 +61,7 @@ async fn call_reduce_closure<'a>(
start: SketchGroup,
reduce_fn: &FunctionParam<'a>,
source_range: SourceRange,
exec_state: &mut ExecState,
) -> Result<SketchGroup, KclError> {
// Call the reduce fn for this repetition.
let reduce_fn_args = vec![
@ -70,7 +71,7 @@ async fn call_reduce_closure<'a>(
}),
KclValue::new_user_val(start.meta.clone(), start),
];
let transform_fn_return = reduce_fn.call(reduce_fn_args).await?;
let transform_fn_return = reduce_fn.call(exec_state, reduce_fn_args).await?;
// Unpack the returned transform object.
let source_ranges = vec![source_range];

View File

@ -6,7 +6,7 @@ use schemars::JsonSchema;
use crate::{
errors::{KclError, KclErrorDetails},
executor::KclValue,
executor::{ExecState, KclValue},
std::Args,
};
@ -22,7 +22,7 @@ async fn _assert(value: bool, message: &str, args: &Args) -> Result<(), KclError
/// Check that the provided value is true, or raise a [KclError]
/// with the provided description.
pub async fn assert(args: Args) -> Result<KclValue, KclError> {
pub async fn assert(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (data, description): (bool, String) = args.get_data()?;
inner_assert(data, &description, &args).await?;
args.make_null_user_val()
@ -42,7 +42,7 @@ async fn inner_assert(data: bool, message: &str, args: &Args) -> Result<(), KclE
_assert(data, message, args).await
}
pub async fn assert_lt(args: Args) -> Result<KclValue, KclError> {
pub async fn assert_lt(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (left, right, description): (f64, f64, String) = args.get_data()?;
inner_assert_lt(left, right, &description, &args).await?;
args.make_null_user_val()
@ -61,7 +61,7 @@ async fn inner_assert_lt(left: f64, right: f64, message: &str, args: &Args) -> R
_assert(left < right, message, args).await
}
pub async fn assert_gt(args: Args) -> Result<KclValue, KclError> {
pub async fn assert_gt(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (left, right, description): (f64, f64, String) = args.get_data()?;
inner_assert_gt(left, right, &description, &args).await?;
args.make_null_user_val()
@ -89,7 +89,7 @@ async fn inner_assert_equal(left: f64, right: f64, epsilon: f64, message: &str,
}
}
pub async fn assert_equal(args: Args) -> Result<KclValue, KclError> {
pub async fn assert_equal(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (left, right, epsilon, description): (f64, f64, f64, String) = args.get_data()?;
inner_assert_equal(left, right, epsilon, &description, &args).await?;
args.make_null_user_val()
@ -108,7 +108,7 @@ async fn inner_assert_gt(left: f64, right: f64, message: &str, args: &Args) -> R
_assert(left > right, message, args).await
}
pub async fn assert_lte(args: Args) -> Result<KclValue, KclError> {
pub async fn assert_lte(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (left, right, description): (f64, f64, String) = args.get_data()?;
inner_assert_lte(left, right, &description, &args).await?;
args.make_null_user_val()
@ -128,7 +128,7 @@ async fn inner_assert_lte(left: f64, right: f64, message: &str, args: &Args) ->
_assert(left <= right, message, args).await
}
pub async fn assert_gte(args: Args) -> Result<KclValue, KclError> {
pub async fn assert_gte(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (left, right, description): (f64, f64, String) = args.get_data()?;
inner_assert_gte(left, right, &description, &args).await?;
args.make_null_user_val()

View File

@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize};
use crate::{
ast::types::TagDeclarator,
errors::{KclError, KclErrorDetails},
executor::{ChamferSurface, EdgeCut, ExtrudeGroup, ExtrudeSurface, GeoMeta, KclValue},
executor::{ChamferSurface, EdgeCut, ExecState, ExtrudeGroup, ExtrudeSurface, GeoMeta, KclValue},
std::{fillet::EdgeReference, Args},
};
@ -27,11 +27,11 @@ pub struct ChamferData {
}
/// Create chamfers on tagged paths.
pub async fn chamfer(args: Args) -> Result<KclValue, KclError> {
pub async fn chamfer(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (data, extrude_group, tag): (ChamferData, Box<ExtrudeGroup>, Option<TagDeclarator>) =
args.get_data_and_extrude_group_and_tag()?;
let extrude_group = inner_chamfer(data, extrude_group, tag, args).await?;
let extrude_group = inner_chamfer(data, extrude_group, tag, exec_state, args).await?;
Ok(KclValue::ExtrudeGroup(extrude_group))
}
@ -103,6 +103,7 @@ async fn inner_chamfer(
data: ChamferData,
extrude_group: Box<ExtrudeGroup>,
tag: Option<TagDeclarator>,
exec_state: &mut ExecState,
args: Args,
) -> Result<Box<ExtrudeGroup>, KclError> {
// Check if tags contains any duplicate values.
@ -130,7 +131,7 @@ async fn inner_chamfer(
for edge_tag in data.tags {
let edge_id = match edge_tag {
EdgeReference::Uuid(uuid) => uuid,
EdgeReference::Tag(edge_tag) => args.get_tag_engine_info(&edge_tag)?.id,
EdgeReference::Tag(edge_tag) => args.get_tag_engine_info(exec_state, &edge_tag)?.id,
};
let id = uuid::Uuid::new_v4();

View File

@ -5,7 +5,7 @@ use schemars::JsonSchema;
use crate::{
errors::{KclError, KclErrorDetails},
executor::{KclValue, SourceRange},
executor::{ExecState, KclValue, SourceRange},
std::Args,
};
@ -31,7 +31,7 @@ impl ConversionError {
}
/// Converts a number to integer.
pub async fn int(args: Args) -> Result<KclValue, KclError> {
pub async fn int(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let num = args.get_number()?;
let converted = inner_int(num).map_err(|err| err.into_kcl_error(args.source_range))?;

View File

@ -11,14 +11,14 @@ use uuid::Uuid;
use crate::{
errors::{KclError, KclErrorDetails},
executor::{
ExtrudeGroup, ExtrudeGroupSet, ExtrudeSurface, GeoMeta, KclValue, Path, SketchGroup, SketchGroupSet,
ExecState, ExtrudeGroup, ExtrudeGroupSet, ExtrudeSurface, GeoMeta, KclValue, Path, SketchGroup, SketchGroupSet,
SketchSurface,
},
std::Args,
};
/// Extrudes by a given amount.
pub async fn extrude(args: Args) -> Result<KclValue, KclError> {
pub async fn extrude(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (length, sketch_group_set) = args.get_number_sketch_group_set()?;
let result = inner_extrude(length, sketch_group_set, args).await?;

View File

@ -10,7 +10,9 @@ use uuid::Uuid;
use crate::{
ast::types::TagDeclarator,
errors::{KclError, KclErrorDetails},
executor::{EdgeCut, ExtrudeGroup, ExtrudeSurface, FilletSurface, GeoMeta, KclValue, TagIdentifier, UserVal},
executor::{
EdgeCut, ExecState, ExtrudeGroup, ExtrudeSurface, FilletSurface, GeoMeta, KclValue, TagIdentifier, UserVal,
},
settings::types::UnitLength,
std::Args,
};
@ -41,11 +43,11 @@ pub enum EdgeReference {
}
/// Create fillets on tagged paths.
pub async fn fillet(args: Args) -> Result<KclValue, KclError> {
pub async fn fillet(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (data, extrude_group, tag): (FilletData, Box<ExtrudeGroup>, Option<TagDeclarator>) =
args.get_data_and_extrude_group_and_tag()?;
let extrude_group = inner_fillet(data, extrude_group, tag, args).await?;
let extrude_group = inner_fillet(data, extrude_group, tag, exec_state, args).await?;
Ok(KclValue::ExtrudeGroup(extrude_group))
}
@ -112,6 +114,7 @@ async fn inner_fillet(
data: FilletData,
extrude_group: Box<ExtrudeGroup>,
tag: Option<TagDeclarator>,
exec_state: &mut ExecState,
args: Args,
) -> Result<Box<ExtrudeGroup>, KclError> {
// Check if tags contains any duplicate values.
@ -130,7 +133,7 @@ async fn inner_fillet(
for edge_tag in data.tags {
let edge_id = match edge_tag {
EdgeReference::Uuid(uuid) => uuid,
EdgeReference::Tag(edge_tag) => args.get_tag_engine_info(&edge_tag)?.id,
EdgeReference::Tag(edge_tag) => args.get_tag_engine_info(exec_state, &edge_tag)?.id,
};
let id = uuid::Uuid::new_v4();
@ -172,10 +175,10 @@ async fn inner_fillet(
}
/// Get the opposite edge to the edge given.
pub async fn get_opposite_edge(args: Args) -> Result<KclValue, KclError> {
pub async fn get_opposite_edge(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let tag: TagIdentifier = args.get_data()?;
let edge = inner_get_opposite_edge(tag, args.clone()).await?;
let edge = inner_get_opposite_edge(tag, exec_state, args.clone()).await?;
Ok(KclValue::UserVal(UserVal {
value: serde_json::to_value(edge).map_err(|e| {
KclError::Type(KclErrorDetails {
@ -217,13 +220,13 @@ pub async fn get_opposite_edge(args: Args) -> Result<KclValue, KclError> {
#[stdlib {
name = "getOppositeEdge",
}]
async fn inner_get_opposite_edge(tag: TagIdentifier, args: Args) -> Result<Uuid, KclError> {
async fn inner_get_opposite_edge(tag: TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<Uuid, KclError> {
if args.ctx.is_mock {
return Ok(Uuid::new_v4());
}
let tagged_path = args.get_tag_engine_info(&tag)?;
let face_id = args.get_adjacent_face_to_tag(exec_state, &tag, false).await?;
let face_id = args.get_adjacent_face_to_tag(&tag, false).await?;
let tagged_path = args.get_tag_engine_info(exec_state, &tag)?;
let resp = args
.send_modeling_cmd(
@ -249,10 +252,10 @@ async fn inner_get_opposite_edge(tag: TagIdentifier, args: Args) -> Result<Uuid,
}
/// Get the next adjacent edge to the edge given.
pub async fn get_next_adjacent_edge(args: Args) -> Result<KclValue, KclError> {
pub async fn get_next_adjacent_edge(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let tag: TagIdentifier = args.get_data()?;
let edge = inner_get_next_adjacent_edge(tag, args.clone()).await?;
let edge = inner_get_next_adjacent_edge(tag, exec_state, args.clone()).await?;
Ok(KclValue::UserVal(UserVal {
value: serde_json::to_value(edge).map_err(|e| {
KclError::Type(KclErrorDetails {
@ -294,13 +297,17 @@ pub async fn get_next_adjacent_edge(args: Args) -> Result<KclValue, KclError> {
#[stdlib {
name = "getNextAdjacentEdge",
}]
async fn inner_get_next_adjacent_edge(tag: TagIdentifier, args: Args) -> Result<Uuid, KclError> {
async fn inner_get_next_adjacent_edge(
tag: TagIdentifier,
exec_state: &mut ExecState,
args: Args,
) -> Result<Uuid, KclError> {
if args.ctx.is_mock {
return Ok(Uuid::new_v4());
}
let tagged_path = args.get_tag_engine_info(&tag)?;
let face_id = args.get_adjacent_face_to_tag(exec_state, &tag, false).await?;
let face_id = args.get_adjacent_face_to_tag(&tag, false).await?;
let tagged_path = args.get_tag_engine_info(exec_state, &tag)?;
let resp = args
.send_modeling_cmd(
@ -331,10 +338,10 @@ async fn inner_get_next_adjacent_edge(tag: TagIdentifier, args: Args) -> Result<
}
/// Get the previous adjacent edge to the edge given.
pub async fn get_previous_adjacent_edge(args: Args) -> Result<KclValue, KclError> {
pub async fn get_previous_adjacent_edge(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let tag: TagIdentifier = args.get_data()?;
let edge = inner_get_previous_adjacent_edge(tag, args.clone()).await?;
let edge = inner_get_previous_adjacent_edge(tag, exec_state, args.clone()).await?;
Ok(KclValue::UserVal(UserVal {
value: serde_json::to_value(edge).map_err(|e| {
KclError::Type(KclErrorDetails {
@ -376,13 +383,17 @@ pub async fn get_previous_adjacent_edge(args: Args) -> Result<KclValue, KclError
#[stdlib {
name = "getPreviousAdjacentEdge",
}]
async fn inner_get_previous_adjacent_edge(tag: TagIdentifier, args: Args) -> Result<Uuid, KclError> {
async fn inner_get_previous_adjacent_edge(
tag: TagIdentifier,
exec_state: &mut ExecState,
args: Args,
) -> Result<Uuid, KclError> {
if args.ctx.is_mock {
return Ok(Uuid::new_v4());
}
let tagged_path = args.get_tag_engine_info(&tag)?;
let face_id = args.get_adjacent_face_to_tag(exec_state, &tag, false).await?;
let face_id = args.get_adjacent_face_to_tag(&tag, false).await?;
let tagged_path = args.get_tag_engine_info(exec_state, &tag)?;
let resp = args
.send_modeling_cmd(

View File

@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
use crate::{
errors::KclError,
executor::{ExtrudeGroup, KclValue},
executor::{ExecState, ExtrudeGroup, KclValue},
std::Args,
};
@ -31,7 +31,7 @@ pub struct HelixData {
}
/// Create a helix on a cylinder.
pub async fn helix(args: Args) -> Result<KclValue, KclError> {
pub async fn helix(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (data, extrude_group): (HelixData, Box<ExtrudeGroup>) = args.get_data_and_extrude_group()?;
let extrude_group = inner_helix(data, extrude_group, args).await?;

View File

@ -9,7 +9,7 @@ use schemars::JsonSchema;
use crate::{
errors::{KclError, KclErrorDetails},
executor::{ImportedGeometry, KclValue},
executor::{ExecState, ImportedGeometry, KclValue},
fs::FileSystem,
std::Args,
};
@ -117,7 +117,7 @@ impl From<ImportFormat> for kittycad::types::InputFormat {
///
/// Import paths are relative to the current project directory. This only works in the desktop app
/// not in browser.
pub async fn import(args: Args) -> Result<KclValue, KclError> {
pub async fn import(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (file_path, options): (String, Option<ImportFormat>) = args.get_import_data()?;
let imported_geometry = inner_import(file_path, options, args).await?;

View File

@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
use crate::{
errors::{KclError, KclErrorDetails},
executor::{ExtrudeGroup, KclValue, SketchGroup},
executor::{ExecState, ExtrudeGroup, KclValue, SketchGroup},
std::{extrude::do_post_extrude, fillet::default_tolerance, Args},
};
@ -49,7 +49,7 @@ impl Default for LoftData {
}
/// Create a 3D surface or solid by interpolating between two or more sketches.
pub async fn loft(args: Args) -> Result<KclValue, KclError> {
pub async fn loft(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (sketch_groups, data): (Vec<SketchGroup>, Option<LoftData>) = args.get_sketch_groups_and_data()?;
let extrude_group = inner_loft(sketch_groups, data, args).await?;

View File

@ -6,12 +6,12 @@ use schemars::JsonSchema;
use crate::{
errors::{KclError, KclErrorDetails},
executor::KclValue,
executor::{ExecState, KclValue},
std::Args,
};
/// Compute the cosine of a number (in radians).
pub async fn cos(args: Args) -> Result<KclValue, KclError> {
pub async fn cos(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let num = args.get_number()?;
let result = inner_cos(num)?;
@ -41,7 +41,7 @@ fn inner_cos(num: f64) -> Result<f64, KclError> {
}
/// Compute the sine of a number (in radians).
pub async fn sin(args: Args) -> Result<KclValue, KclError> {
pub async fn sin(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let num = args.get_number()?;
let result = inner_sin(num)?;
@ -71,7 +71,7 @@ fn inner_sin(num: f64) -> Result<f64, KclError> {
}
/// Compute the tangent of a number (in radians).
pub async fn tan(args: Args) -> Result<KclValue, KclError> {
pub async fn tan(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let num = args.get_number()?;
let result = inner_tan(num)?;
@ -101,7 +101,7 @@ fn inner_tan(num: f64) -> Result<f64, KclError> {
}
/// Return the value of `pi`. Archimedes constant (π).
pub async fn pi(args: Args) -> Result<KclValue, KclError> {
pub async fn pi(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let result = inner_pi()?;
args.make_user_val_from_f64(result)
@ -126,7 +126,7 @@ fn inner_pi() -> Result<f64, KclError> {
}
/// Compute the square root of a number.
pub async fn sqrt(args: Args) -> Result<KclValue, KclError> {
pub async fn sqrt(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let num = args.get_number()?;
let result = inner_sqrt(num)?;
@ -156,7 +156,7 @@ fn inner_sqrt(num: f64) -> Result<f64, KclError> {
}
/// Compute the absolute value of a number.
pub async fn abs(args: Args) -> Result<KclValue, KclError> {
pub async fn abs(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let num = args.get_number()?;
let result = inner_abs(num)?;
@ -193,7 +193,7 @@ fn inner_abs(num: f64) -> Result<f64, KclError> {
}
/// Compute the largest integer less than or equal to a number.
pub async fn floor(args: Args) -> Result<KclValue, KclError> {
pub async fn floor(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let num = args.get_number()?;
let result = inner_floor(num)?;
@ -221,7 +221,7 @@ fn inner_floor(num: f64) -> Result<f64, KclError> {
}
/// Compute the smallest integer greater than or equal to a number.
pub async fn ceil(args: Args) -> Result<KclValue, KclError> {
pub async fn ceil(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let num = args.get_number()?;
let result = inner_ceil(num)?;
@ -249,7 +249,7 @@ fn inner_ceil(num: f64) -> Result<f64, KclError> {
}
/// Compute the minimum of the given arguments.
pub async fn min(args: Args) -> Result<KclValue, KclError> {
pub async fn min(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let nums = args.get_number_array()?;
let result = inner_min(nums);
@ -286,7 +286,7 @@ fn inner_min(args: Vec<f64>) -> f64 {
}
/// Compute the maximum of the given arguments.
pub async fn max(args: Args) -> Result<KclValue, KclError> {
pub async fn max(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let nums = args.get_number_array()?;
let result = inner_max(nums);
@ -323,7 +323,7 @@ fn inner_max(args: Vec<f64>) -> f64 {
}
/// Compute the number to a power.
pub async fn pow(args: Args) -> Result<KclValue, KclError> {
pub async fn pow(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let nums = args.get_number_array()?;
if nums.len() > 2 {
return Err(KclError::Type(KclErrorDetails {
@ -367,7 +367,7 @@ fn inner_pow(num: f64, pow: f64) -> Result<f64, KclError> {
}
/// Compute the arccosine of a number (in radians).
pub async fn acos(args: Args) -> Result<KclValue, KclError> {
pub async fn acos(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let num = args.get_number()?;
let result = inner_acos(num)?;
@ -398,7 +398,7 @@ fn inner_acos(num: f64) -> Result<f64, KclError> {
}
/// Compute the arcsine of a number (in radians).
pub async fn asin(args: Args) -> Result<KclValue, KclError> {
pub async fn asin(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let num = args.get_number()?;
let result = inner_asin(num)?;
@ -428,7 +428,7 @@ fn inner_asin(num: f64) -> Result<f64, KclError> {
}
/// Compute the arctangent of a number (in radians).
pub async fn atan(args: Args) -> Result<KclValue, KclError> {
pub async fn atan(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let num = args.get_number()?;
let result = inner_atan(num)?;
@ -462,7 +462,7 @@ fn inner_atan(num: f64) -> Result<f64, KclError> {
/// The result might not be correctly rounded owing to implementation
/// details; `log2()` can produce more accurate results for base 2,
/// and `log10()` can produce more accurate results for base 10.
pub async fn log(args: Args) -> Result<KclValue, KclError> {
pub async fn log(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let nums = args.get_number_array()?;
if nums.len() > 2 {
return Err(KclError::Type(KclErrorDetails {
@ -507,7 +507,7 @@ fn inner_log(num: f64, base: f64) -> Result<f64, KclError> {
}
/// Compute the base 2 logarithm of the number.
pub async fn log2(args: Args) -> Result<KclValue, KclError> {
pub async fn log2(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let num = args.get_number()?;
let result = inner_log2(num)?;
@ -535,7 +535,7 @@ fn inner_log2(num: f64) -> Result<f64, KclError> {
}
/// Compute the base 10 logarithm of the number.
pub async fn log10(args: Args) -> Result<KclValue, KclError> {
pub async fn log10(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let num = args.get_number()?;
let result = inner_log10(num)?;
@ -563,7 +563,7 @@ fn inner_log10(num: f64) -> Result<f64, KclError> {
}
/// Compute the natural logarithm of the number.
pub async fn ln(args: Args) -> Result<KclValue, KclError> {
pub async fn ln(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let num = args.get_number()?;
let result = inner_ln(num)?;
@ -591,7 +591,7 @@ fn inner_ln(num: f64) -> Result<f64, KclError> {
}
/// Return the value of Eulers number `e`.
pub async fn e(args: Args) -> Result<KclValue, KclError> {
pub async fn e(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let result = inner_e()?;
args.make_user_val_from_f64(result)
@ -620,7 +620,7 @@ fn inner_e() -> Result<f64, KclError> {
}
/// Return the value of `tau`. The full circle constant (τ). Equal to 2π.
pub async fn tau(args: Args) -> Result<KclValue, KclError> {
pub async fn tau(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let result = inner_tau()?;
args.make_user_val_from_f64(result)
@ -649,7 +649,7 @@ fn inner_tau() -> Result<f64, KclError> {
}
/// Converts a number from degrees to radians.
pub async fn to_radians(args: Args) -> Result<KclValue, KclError> {
pub async fn to_radians(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let num = args.get_number()?;
let result = inner_to_radians(num)?;
@ -679,7 +679,7 @@ fn inner_to_radians(num: f64) -> Result<f64, KclError> {
}
/// Converts a number from radians to degrees.
pub async fn to_degrees(args: Args) -> Result<KclValue, KclError> {
pub async fn to_degrees(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let num = args.get_number()?;
let result = inner_to_degrees(num)?;

View File

@ -38,11 +38,14 @@ use crate::{
ast::types::FunctionExpression,
docs::StdLibFn,
errors::KclError,
executor::{KclValue, ProgramMemory, SketchGroup, SketchSurface},
executor::{ExecState, KclValue, ProgramMemory, SketchGroup, SketchSurface},
std::kcl_stdlib::KclStdLibFn,
};
pub type StdFn = fn(Args) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<KclValue, KclError>> + Send>>;
pub type StdFn = fn(
&mut ExecState,
Args,
) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<KclValue, KclError>> + Send + '_>>;
pub type FnMap = HashMap<String, StdFn>;
@ -228,7 +231,7 @@ pub enum FunctionKind {
}
/// Compute the length of the given leg.
pub async fn leg_length(args: Args) -> Result<KclValue, KclError> {
pub async fn leg_length(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (hypotenuse, leg) = args.get_hypotenuse_leg()?;
let result = inner_leg_length(hypotenuse, leg);
args.make_user_val_from_f64(result)
@ -248,7 +251,7 @@ fn inner_leg_length(hypotenuse: f64, leg: f64) -> f64 {
}
/// Compute the angle of the given leg for x.
pub async fn leg_angle_x(args: Args) -> Result<KclValue, KclError> {
pub async fn leg_angle_x(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (hypotenuse, leg) = args.get_hypotenuse_leg()?;
let result = inner_leg_angle_x(hypotenuse, leg);
args.make_user_val_from_f64(result)
@ -268,7 +271,7 @@ fn inner_leg_angle_x(hypotenuse: f64, leg: f64) -> f64 {
}
/// Compute the angle of the given leg for y.
pub async fn leg_angle_y(args: Args) -> Result<KclValue, KclError> {
pub async fn leg_angle_y(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (hypotenuse, leg) = args.get_hypotenuse_leg()?;
let result = inner_leg_angle_y(hypotenuse, leg);
args.make_user_val_from_f64(result)
@ -302,8 +305,9 @@ pub enum Primitive {
Uuid,
}
/// A closure used as an argument to a stdlib function.
pub struct FnAsArg<'a> {
pub func: &'a crate::executor::MemoryFunction,
pub func: Option<&'a crate::executor::MemoryFunction>,
pub expr: Box<FunctionExpression>,
pub memory: Box<ProgramMemory>,
}

View File

@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize};
use crate::{
errors::{KclError, KclErrorDetails},
executor::{
ExtrudeGroup, ExtrudeGroupSet, Geometries, Geometry, KclValue, Point3d, SketchGroup, SketchGroupSet,
ExecState, ExtrudeGroup, ExtrudeGroupSet, Geometries, Geometry, KclValue, Point3d, SketchGroup, SketchGroupSet,
SourceRange, UserVal,
},
function_param::FunctionParam,
@ -77,7 +77,7 @@ impl LinearPattern {
/// A linear pattern
/// Each element in the pattern repeats a particular piece of geometry.
/// The repetitions can be transformed by the `transform` parameter.
pub async fn pattern_transform(args: Args) -> Result<KclValue, KclError> {
pub async fn pattern_transform(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (num_repetitions, transform, extr) = args.get_pattern_transform_args()?;
let extrude_groups = inner_pattern_transform(
@ -88,9 +88,9 @@ pub async fn pattern_transform(args: Args) -> Result<KclValue, KclError> {
meta: vec![args.source_range.into()],
ctx: args.ctx.clone(),
memory: *transform.memory,
dynamic_state: args.dynamic_state.clone(),
},
extr,
exec_state,
&args,
)
.await?;
@ -131,18 +131,19 @@ async fn inner_pattern_transform<'a>(
num_repetitions: u32,
transform_function: FunctionParam<'a>,
extrude_group_set: ExtrudeGroupSet,
exec_state: &mut ExecState,
args: &'a Args,
) -> Result<Vec<Box<ExtrudeGroup>>, KclError> {
// Build the vec of transforms, one for each repetition.
let mut transform = Vec::with_capacity(usize::try_from(num_repetitions).unwrap());
for i in 0..num_repetitions {
let t = make_transform(i, &transform_function, args.source_range).await?;
let t = make_transform(i, &transform_function, args.source_range, exec_state).await?;
transform.push(t);
}
// Flush the batch for our fillets/chamfers if there are any.
// If we do not flush these, then you won't be able to pattern something with fillets.
// Flush just the fillets/chamfers that apply to these extrude groups.
args.flush_batch_for_extrude_group_set(extrude_group_set.clone().into())
args.flush_batch_for_extrude_group_set(exec_state, extrude_group_set.clone().into())
.await?;
let starting_extrude_groups: Vec<Box<ExtrudeGroup>> = extrude_group_set.into();
@ -201,6 +202,7 @@ async fn make_transform<'a>(
i: u32,
transform_function: &FunctionParam<'a>,
source_range: SourceRange,
exec_state: &mut ExecState,
) -> Result<kittycad::types::Transform, KclError> {
// Call the transform fn for this repetition.
let repetition_num = KclValue::UserVal(UserVal {
@ -208,7 +210,7 @@ async fn make_transform<'a>(
meta: vec![source_range.into()],
});
let transform_fn_args = vec![repetition_num];
let transform_fn_return = transform_function.call(transform_fn_args).await?;
let transform_fn_return = transform_function.call(exec_state, transform_fn_args).await?;
// Unpack the returned transform object.
let source_ranges = vec![source_range];
@ -299,7 +301,7 @@ mod tests {
}
/// A linear pattern on a 2D sketch.
pub async fn pattern_linear_2d(args: Args) -> Result<KclValue, KclError> {
pub async fn pattern_linear_2d(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (data, sketch_group_set): (LinearPattern2dData, SketchGroupSet) = args.get_data_and_sketch_group_set()?;
if data.axis == [0.0, 0.0] {
@ -366,7 +368,7 @@ async fn inner_pattern_linear_2d(
}
/// A linear pattern on a 3D model.
pub async fn pattern_linear_3d(args: Args) -> Result<KclValue, KclError> {
pub async fn pattern_linear_3d(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (data, extrude_group_set): (LinearPattern3dData, ExtrudeGroupSet) = args.get_data_and_extrude_group_set()?;
if data.axis == [0.0, 0.0, 0.0] {
@ -378,7 +380,7 @@ pub async fn pattern_linear_3d(args: Args) -> Result<KclValue, KclError> {
}));
}
let extrude_groups = inner_pattern_linear_3d(data, extrude_group_set, args).await?;
let extrude_groups = inner_pattern_linear_3d(data, extrude_group_set, exec_state, args).await?;
Ok(extrude_groups.into())
}
@ -406,12 +408,13 @@ pub async fn pattern_linear_3d(args: Args) -> Result<KclValue, KclError> {
async fn inner_pattern_linear_3d(
data: LinearPattern3dData,
extrude_group_set: ExtrudeGroupSet,
exec_state: &mut ExecState,
args: Args,
) -> Result<Vec<Box<ExtrudeGroup>>, KclError> {
// Flush the batch for our fillets/chamfers if there are any.
// If we do not flush these, then you won't be able to pattern something with fillets.
// Flush just the fillets/chamfers that apply to these extrude groups.
args.flush_batch_for_extrude_group_set(extrude_group_set.clone().into())
args.flush_batch_for_extrude_group_set(exec_state, extrude_group_set.clone().into())
.await?;
let starting_extrude_groups: Vec<Box<ExtrudeGroup>> = extrude_group_set.into();
@ -574,7 +577,7 @@ impl CircularPattern {
}
/// A circular pattern on a 2D sketch.
pub async fn pattern_circular_2d(args: Args) -> Result<KclValue, KclError> {
pub async fn pattern_circular_2d(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (data, sketch_group_set): (CircularPattern2dData, SketchGroupSet) = args.get_data_and_sketch_group_set()?;
let sketch_groups = inner_pattern_circular_2d(data, sketch_group_set, args).await?;
@ -639,10 +642,10 @@ async fn inner_pattern_circular_2d(
}
/// A circular pattern on a 3D model.
pub async fn pattern_circular_3d(args: Args) -> Result<KclValue, KclError> {
pub async fn pattern_circular_3d(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (data, extrude_group_set): (CircularPattern3dData, ExtrudeGroupSet) = args.get_data_and_extrude_group_set()?;
let extrude_groups = inner_pattern_circular_3d(data, extrude_group_set, args).await?;
let extrude_groups = inner_pattern_circular_3d(data, extrude_group_set, exec_state, args).await?;
Ok(extrude_groups.into())
}
@ -670,12 +673,13 @@ pub async fn pattern_circular_3d(args: Args) -> Result<KclValue, KclError> {
async fn inner_pattern_circular_3d(
data: CircularPattern3dData,
extrude_group_set: ExtrudeGroupSet,
exec_state: &mut ExecState,
args: Args,
) -> Result<Vec<Box<ExtrudeGroup>>, KclError> {
// Flush the batch for our fillets/chamfers if there are any.
// If we do not flush these, then you won't be able to pattern something with fillets.
// Flush just the fillets/chamfers that apply to these extrude groups.
args.flush_batch_for_extrude_group_set(extrude_group_set.clone().into())
args.flush_batch_for_extrude_group_set(exec_state, extrude_group_set.clone().into())
.await?;
let starting_extrude_groups: Vec<Box<ExtrudeGroup>> = extrude_group_set.into();

View File

@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize};
use crate::{
errors::KclError,
executor::{KclValue, Metadata, Plane, UserVal},
executor::{ExecState, KclValue, Metadata, Plane, UserVal},
std::{sketch::PlaneData, Args},
};
@ -48,7 +48,7 @@ impl From<StandardPlane> for PlaneData {
}
/// Offset a plane by a distance along its normal.
pub async fn offset_plane(args: Args) -> Result<KclValue, KclError> {
pub async fn offset_plane(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (std_plane, offset): (StandardPlane, f64) = args.get_data_and_float()?;
let plane = inner_offset_plane(std_plane, offset).await?;

View File

@ -5,7 +5,11 @@ use derive_docs::stdlib;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::{errors::KclError, executor::KclValue, std::Args};
use crate::{
errors::KclError,
executor::{ExecState, KclValue},
std::Args,
};
/// Data for polar coordinates.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
@ -19,7 +23,7 @@ pub struct PolarCoordsData {
}
/// Convert from polar/sphere coordinates to cartesian coordinates.
pub async fn polar(args: Args) -> Result<KclValue, KclError> {
pub async fn polar(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let data: PolarCoordsData = args.get_data()?;
let result = inner_polar(&data)?;

View File

@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
use crate::{
errors::{KclError, KclErrorDetails},
executor::{ExtrudeGroup, KclValue, SketchGroup},
executor::{ExecState, ExtrudeGroup, KclValue, SketchGroup},
std::{
extrude::do_post_extrude,
fillet::{default_tolerance, EdgeReference},
@ -101,10 +101,10 @@ impl RevolveAxisAndOrigin {
}
/// Revolve a sketch around an axis.
pub async fn revolve(args: Args) -> Result<KclValue, KclError> {
pub async fn revolve(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (data, sketch_group): (RevolveData, SketchGroup) = args.get_data_and_sketch_group()?;
let extrude_group = inner_revolve(data, sketch_group, args).await?;
let extrude_group = inner_revolve(data, sketch_group, exec_state, args).await?;
Ok(KclValue::ExtrudeGroup(extrude_group))
}
@ -250,6 +250,7 @@ pub async fn revolve(args: Args) -> Result<KclValue, KclError> {
async fn inner_revolve(
data: RevolveData,
sketch_group: SketchGroup,
exec_state: &mut ExecState,
args: Args,
) -> Result<Box<ExtrudeGroup>, KclError> {
if let Some(angle) = data.angle {
@ -284,7 +285,7 @@ async fn inner_revolve(
RevolveAxis::Edge(edge) => {
let edge_id = match edge {
EdgeReference::Uuid(uuid) => uuid,
EdgeReference::Tag(tag) => args.get_tag_engine_info(&tag)?.id,
EdgeReference::Tag(tag) => args.get_tag_engine_info(exec_state, &tag)?.id,
};
args.batch_modeling_cmd(
id,

View File

@ -6,14 +6,14 @@ use schemars::JsonSchema;
use crate::{
errors::{KclError, KclErrorDetails},
executor::{KclValue, SketchGroup, TagIdentifier},
executor::{ExecState, KclValue, SketchGroup, TagIdentifier},
std::{utils::between, Args},
};
/// Returns the segment end of x.
pub async fn segment_end_x(args: Args) -> Result<KclValue, KclError> {
pub async fn segment_end_x(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let tag: TagIdentifier = args.get_data()?;
let result = inner_segment_end_x(&tag, args.clone())?;
let result = inner_segment_end_x(&tag, exec_state, args.clone())?;
args.make_user_val_from_f64(result)
}
@ -34,8 +34,8 @@ pub async fn segment_end_x(args: Args) -> Result<KclValue, KclError> {
#[stdlib {
name = "segEndX",
}]
fn inner_segment_end_x(tag: &TagIdentifier, args: Args) -> Result<f64, KclError> {
let line = args.get_tag_engine_info(tag)?;
fn inner_segment_end_x(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<f64, KclError> {
let line = args.get_tag_engine_info(exec_state, tag)?;
let path = line.path.clone().ok_or_else(|| {
KclError::Type(KclErrorDetails {
message: format!("Expected a line segment with a path, found `{:?}`", line),
@ -47,9 +47,9 @@ fn inner_segment_end_x(tag: &TagIdentifier, args: Args) -> Result<f64, KclError>
}
/// Returns the segment end of y.
pub async fn segment_end_y(args: Args) -> Result<KclValue, KclError> {
pub async fn segment_end_y(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let tag: TagIdentifier = args.get_data()?;
let result = inner_segment_end_y(&tag, args.clone())?;
let result = inner_segment_end_y(&tag, exec_state, args.clone())?;
args.make_user_val_from_f64(result)
}
@ -71,8 +71,8 @@ pub async fn segment_end_y(args: Args) -> Result<KclValue, KclError> {
#[stdlib {
name = "segEndY",
}]
fn inner_segment_end_y(tag: &TagIdentifier, args: Args) -> Result<f64, KclError> {
let line = args.get_tag_engine_info(tag)?;
fn inner_segment_end_y(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<f64, KclError> {
let line = args.get_tag_engine_info(exec_state, tag)?;
let path = line.path.clone().ok_or_else(|| {
KclError::Type(KclErrorDetails {
message: format!("Expected a line segment with a path, found `{:?}`", line),
@ -84,7 +84,7 @@ fn inner_segment_end_y(tag: &TagIdentifier, args: Args) -> Result<f64, KclError>
}
/// Returns the last segment of x.
pub async fn last_segment_x(args: Args) -> Result<KclValue, KclError> {
pub async fn last_segment_x(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let sketch_group = args.get_sketch_group()?;
let result = inner_last_segment_x(sketch_group, args.clone())?;
@ -127,7 +127,7 @@ fn inner_last_segment_x(sketch_group: SketchGroup, args: Args) -> Result<f64, Kc
}
/// Returns the last segment of y.
pub async fn last_segment_y(args: Args) -> Result<KclValue, KclError> {
pub async fn last_segment_y(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let sketch_group = args.get_sketch_group()?;
let result = inner_last_segment_y(sketch_group, args.clone())?;
@ -170,9 +170,9 @@ fn inner_last_segment_y(sketch_group: SketchGroup, args: Args) -> Result<f64, Kc
}
/// Returns the length of the segment.
pub async fn segment_length(args: Args) -> Result<KclValue, KclError> {
pub async fn segment_length(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let tag: TagIdentifier = args.get_data()?;
let result = inner_segment_length(&tag, args.clone())?;
let result = inner_segment_length(&tag, exec_state, args.clone())?;
args.make_user_val_from_f64(result)
}
@ -200,8 +200,8 @@ pub async fn segment_length(args: Args) -> Result<KclValue, KclError> {
#[stdlib {
name = "segLen",
}]
fn inner_segment_length(tag: &TagIdentifier, args: Args) -> Result<f64, KclError> {
let line = args.get_tag_engine_info(tag)?;
fn inner_segment_length(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<f64, KclError> {
let line = args.get_tag_engine_info(exec_state, tag)?;
let path = line.path.clone().ok_or_else(|| {
KclError::Type(KclErrorDetails {
message: format!("Expected a line segment with a path, found `{:?}`", line),
@ -215,10 +215,10 @@ fn inner_segment_length(tag: &TagIdentifier, args: Args) -> Result<f64, KclError
}
/// Returns the angle of the segment.
pub async fn segment_angle(args: Args) -> Result<KclValue, KclError> {
pub async fn segment_angle(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let tag: TagIdentifier = args.get_data()?;
let result = inner_segment_angle(&tag, args.clone())?;
let result = inner_segment_angle(&tag, exec_state, args.clone())?;
args.make_user_val_from_f64(result)
}
@ -240,8 +240,8 @@ pub async fn segment_angle(args: Args) -> Result<KclValue, KclError> {
#[stdlib {
name = "segAng",
}]
fn inner_segment_angle(tag: &TagIdentifier, args: Args) -> Result<f64, KclError> {
let line = args.get_tag_engine_info(tag)?;
fn inner_segment_angle(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<f64, KclError> {
let line = args.get_tag_engine_info(exec_state, tag)?;
let path = line.path.clone().ok_or_else(|| {
KclError::Type(KclErrorDetails {
message: format!("Expected a line segment with a path, found `{:?}`", line),
@ -255,9 +255,9 @@ fn inner_segment_angle(tag: &TagIdentifier, args: Args) -> Result<f64, KclError>
}
/// Returns the angle to match the given length for x.
pub async fn angle_to_match_length_x(args: Args) -> Result<KclValue, KclError> {
pub async fn angle_to_match_length_x(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (tag, to, sketch_group) = args.get_tag_to_number_sketch_group()?;
let result = inner_angle_to_match_length_x(&tag, to, sketch_group, args.clone())?;
let result = inner_angle_to_match_length_x(&tag, to, sketch_group, exec_state, args.clone())?;
args.make_user_val_from_f64(result)
}
@ -282,9 +282,10 @@ fn inner_angle_to_match_length_x(
tag: &TagIdentifier,
to: f64,
sketch_group: SketchGroup,
exec_state: &mut ExecState,
args: Args,
) -> Result<f64, KclError> {
let line = args.get_tag_engine_info(tag)?;
let line = args.get_tag_engine_info(exec_state, tag)?;
let path = line.path.clone().ok_or_else(|| {
KclError::Type(KclErrorDetails {
message: format!("Expected a line segment with a path, found `{:?}`", line),
@ -320,9 +321,9 @@ fn inner_angle_to_match_length_x(
}
/// Returns the angle to match the given length for y.
pub async fn angle_to_match_length_y(args: Args) -> Result<KclValue, KclError> {
pub async fn angle_to_match_length_y(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (tag, to, sketch_group) = args.get_tag_to_number_sketch_group()?;
let result = inner_angle_to_match_length_y(&tag, to, sketch_group, args.clone())?;
let result = inner_angle_to_match_length_y(&tag, to, sketch_group, exec_state, args.clone())?;
args.make_user_val_from_f64(result)
}
@ -348,9 +349,10 @@ fn inner_angle_to_match_length_y(
tag: &TagIdentifier,
to: f64,
sketch_group: SketchGroup,
exec_state: &mut ExecState,
args: Args,
) -> Result<f64, KclError> {
let line = args.get_tag_engine_info(tag)?;
let line = args.get_tag_engine_info(exec_state, tag)?;
let path = line.path.clone().ok_or_else(|| {
KclError::Type(KclErrorDetails {
message: format!("Expected a line segment with a path, found `{:?}`", line),

View File

@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
use crate::{
ast::types::TagDeclarator,
errors::KclError,
executor::KclValue,
executor::{ExecState, KclValue},
std::{Args, SketchGroup, SketchSurface},
};
@ -22,11 +22,11 @@ pub enum SketchSurfaceOrGroup {
}
/// Sketch a circle.
pub async fn circle(args: Args) -> Result<KclValue, KclError> {
pub async fn circle(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (center, radius, sketch_surface_or_group, tag): ([f64; 2], f64, SketchSurfaceOrGroup, Option<TagDeclarator>) =
args.get_circle_args()?;
let sketch_group = inner_circle(center, radius, sketch_surface_or_group, tag, args).await?;
let sketch_group = inner_circle(center, radius, sketch_surface_or_group, tag, exec_state, args).await?;
Ok(KclValue::new_user_val(sketch_group.meta.clone(), sketch_group))
}
@ -59,14 +59,20 @@ async fn inner_circle(
radius: f64,
sketch_surface_or_group: SketchSurfaceOrGroup,
tag: Option<TagDeclarator>,
exec_state: &mut ExecState,
args: Args,
) -> Result<SketchGroup, KclError> {
let sketch_surface = match sketch_surface_or_group {
SketchSurfaceOrGroup::SketchSurface(surface) => surface,
SketchSurfaceOrGroup::SketchGroup(group) => group.on,
};
let mut sketch_group =
crate::std::sketch::inner_start_profile_at([center[0] + radius, center[1]], sketch_surface, None, args.clone())
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.

View File

@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
use crate::{
errors::{KclError, KclErrorDetails},
executor::{ExtrudeGroup, KclValue},
executor::{ExecState, ExtrudeGroup, KclValue},
std::{sketch::FaceTag, Args},
};
@ -24,10 +24,10 @@ pub struct ShellData {
}
/// Create a shell.
pub async fn shell(args: Args) -> Result<KclValue, KclError> {
pub async fn shell(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (data, extrude_group): (ShellData, Box<ExtrudeGroup>) = args.get_data_and_extrude_group()?;
let extrude_group = inner_shell(data, extrude_group, args).await?;
let extrude_group = inner_shell(data, extrude_group, exec_state, args).await?;
Ok(KclValue::ExtrudeGroup(extrude_group))
}
@ -154,6 +154,7 @@ pub async fn shell(args: Args) -> Result<KclValue, KclError> {
async fn inner_shell(
data: ShellData,
extrude_group: Box<ExtrudeGroup>,
exec_state: &mut ExecState,
args: Args,
) -> Result<Box<ExtrudeGroup>, KclError> {
if data.faces.is_empty() {
@ -165,7 +166,7 @@ async fn inner_shell(
let mut face_ids = Vec::new();
for tag in data.faces {
let extrude_plane_id = tag.get_face_id(&extrude_group, &args, false).await?;
let extrude_plane_id = tag.get_face_id(&extrude_group, exec_state, &args, false).await?;
face_ids.push(extrude_plane_id);
}
@ -179,7 +180,7 @@ async fn inner_shell(
// Flush the batch for our fillets/chamfers if there are any.
// If we do not do these for sketch on face, things will fail with face does not exist.
args.flush_batch_for_extrude_group_set(extrude_group.clone().into())
args.flush_batch_for_extrude_group_set(exec_state, extrude_group.clone().into())
.await?;
args.batch_modeling_cmd(
@ -197,10 +198,10 @@ async fn inner_shell(
}
/// Make the inside of a 3D object hollow.
pub async fn hollow(args: Args) -> Result<KclValue, KclError> {
pub async fn hollow(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (thickness, extrude_group): (f64, Box<ExtrudeGroup>) = args.get_data_and_extrude_group()?;
let extrude_group = inner_hollow(thickness, extrude_group, args).await?;
let extrude_group = inner_hollow(thickness, extrude_group, exec_state, args).await?;
Ok(KclValue::ExtrudeGroup(extrude_group))
}
@ -236,11 +237,12 @@ pub async fn hollow(args: Args) -> Result<KclValue, KclError> {
async fn inner_hollow(
thickness: f64,
extrude_group: Box<ExtrudeGroup>,
exec_state: &mut ExecState,
args: Args,
) -> Result<Box<ExtrudeGroup>, KclError> {
// Flush the batch for our fillets/chamfers if there are any.
// If we do not do these for sketch on face, things will fail with face does not exist.
args.flush_batch_for_extrude_group_set(extrude_group.clone().into())
args.flush_batch_for_extrude_group_set(exec_state, extrude_group.clone().into())
.await?;
args.batch_modeling_cmd(

View File

@ -13,8 +13,8 @@ use crate::{
ast::types::TagDeclarator,
errors::{KclError, KclErrorDetails},
executor::{
BasePath, ExtrudeGroup, Face, GeoMeta, KclValue, Path, Plane, PlaneType, Point2d, Point3d, SketchGroup,
SketchGroupSet, SketchSurface, TagEngineInfo, TagIdentifier, UserVal,
BasePath, ExecState, ExtrudeGroup, Face, GeoMeta, KclValue, Path, Plane, PlaneType, Point2d, Point3d,
SketchGroup, SketchGroupSet, SketchSurface, TagEngineInfo, TagIdentifier, UserVal,
},
std::{
utils::{
@ -50,11 +50,12 @@ impl FaceTag {
pub async fn get_face_id(
&self,
extrude_group: &ExtrudeGroup,
exec_state: &mut ExecState,
args: &Args,
must_be_planar: bool,
) -> Result<uuid::Uuid, KclError> {
match self {
FaceTag::Tag(ref t) => args.get_adjacent_face_to_tag(t, must_be_planar).await,
FaceTag::Tag(ref t) => args.get_adjacent_face_to_tag(exec_state, t, must_be_planar).await,
FaceTag::StartOrEnd(StartOrEnd::Start) => extrude_group.start_cap_id.ok_or_else(|| {
KclError::Type(KclErrorDetails {
message: "Expected a start face".to_string(),
@ -89,7 +90,7 @@ pub enum StartOrEnd {
}
/// Draw a line to a point.
pub async fn line_to(args: Args) -> Result<KclValue, KclError> {
pub async fn line_to(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (to, sketch_group, tag): ([f64; 2], SketchGroup, Option<TagDeclarator>) =
args.get_data_and_sketch_group_and_tag()?;
@ -160,7 +161,7 @@ async fn inner_line_to(
}
/// Draw a line to a point on the x-axis.
pub async fn x_line_to(args: Args) -> Result<KclValue, KclError> {
pub async fn x_line_to(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (to, sketch_group, tag): (f64, SketchGroup, Option<TagDeclarator>) =
args.get_data_and_sketch_group_and_tag()?;
@ -208,7 +209,7 @@ async fn inner_x_line_to(
}
/// Draw a line to a point on the y-axis.
pub async fn y_line_to(args: Args) -> Result<KclValue, KclError> {
pub async fn y_line_to(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (to, sketch_group, tag): (f64, SketchGroup, Option<TagDeclarator>) =
args.get_data_and_sketch_group_and_tag()?;
@ -248,7 +249,7 @@ async fn inner_y_line_to(
}
/// Draw a line.
pub async fn line(args: Args) -> Result<KclValue, KclError> {
pub async fn line(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (delta, sketch_group, tag): ([f64; 2], SketchGroup, Option<TagDeclarator>) =
args.get_data_and_sketch_group_and_tag()?;
@ -333,7 +334,7 @@ async fn inner_line(
}
/// Draw a line on the x-axis.
pub async fn x_line(args: Args) -> Result<KclValue, KclError> {
pub async fn x_line(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (length, sketch_group, tag): (f64, SketchGroup, Option<TagDeclarator>) =
args.get_data_and_sketch_group_and_tag()?;
@ -376,7 +377,7 @@ async fn inner_x_line(
}
/// Draw a line on the y-axis.
pub async fn y_line(args: Args) -> Result<KclValue, KclError> {
pub async fn y_line(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (length, sketch_group, tag): (f64, SketchGroup, Option<TagDeclarator>) =
args.get_data_and_sketch_group_and_tag()?;
@ -430,7 +431,7 @@ pub enum AngledLineData {
}
/// Draw an angled line.
pub async fn angled_line(args: Args) -> Result<KclValue, KclError> {
pub async fn angled_line(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (data, sketch_group, tag): (AngledLineData, SketchGroup, Option<TagDeclarator>) =
args.get_data_and_sketch_group_and_tag()?;
@ -519,7 +520,7 @@ async fn inner_angled_line(
}
/// Draw an angled line of a given x length.
pub async fn angled_line_of_x_length(args: Args) -> Result<KclValue, KclError> {
pub async fn angled_line_of_x_length(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (data, sketch_group, tag): (AngledLineData, SketchGroup, Option<TagDeclarator>) =
args.get_data_and_sketch_group_and_tag()?;
@ -587,7 +588,7 @@ pub struct AngledLineToData {
}
/// Draw an angled line to a given x coordinate.
pub async fn angled_line_to_x(args: Args) -> Result<KclValue, KclError> {
pub async fn angled_line_to_x(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (data, sketch_group, tag): (AngledLineToData, SketchGroup, Option<TagDeclarator>) =
args.get_data_and_sketch_group_and_tag()?;
@ -644,7 +645,7 @@ async fn inner_angled_line_to_x(
}
/// Draw an angled line of a given y length.
pub async fn angled_line_of_y_length(args: Args) -> Result<KclValue, KclError> {
pub async fn angled_line_of_y_length(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (data, sketch_group, tag): (AngledLineData, SketchGroup, Option<TagDeclarator>) =
args.get_data_and_sketch_group_and_tag()?;
@ -704,7 +705,7 @@ async fn inner_angled_line_of_y_length(
}
/// Draw an angled line to a given y coordinate.
pub async fn angled_line_to_y(args: Args) -> Result<KclValue, KclError> {
pub async fn angled_line_to_y(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (data, sketch_group, tag): (AngledLineToData, SketchGroup, Option<TagDeclarator>) =
args.get_data_and_sketch_group_and_tag()?;
@ -775,10 +776,10 @@ pub struct AngledLineThatIntersectsData {
}
/// Draw an angled line that intersects with a given line.
pub async fn angled_line_that_intersects(args: Args) -> Result<KclValue, KclError> {
pub async fn angled_line_that_intersects(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (data, sketch_group, tag): (AngledLineThatIntersectsData, SketchGroup, Option<TagDeclarator>) =
args.get_data_and_sketch_group_and_tag()?;
let new_sketch_group = inner_angled_line_that_intersects(data, sketch_group, tag, args).await?;
let new_sketch_group = inner_angled_line_that_intersects(data, sketch_group, tag, exec_state, args).await?;
Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group))
}
@ -808,9 +809,10 @@ async fn inner_angled_line_that_intersects(
data: AngledLineThatIntersectsData,
sketch_group: SketchGroup,
tag: Option<TagDeclarator>,
exec_state: &mut ExecState,
args: Args,
) -> Result<SketchGroup, KclError> {
let intersect_path = args.get_tag_engine_info(&data.intersect_tag)?;
let intersect_path = args.get_tag_engine_info(exec_state, &data.intersect_tag)?;
let path = intersect_path.path.clone().ok_or_else(|| {
KclError::Type(KclErrorDetails {
message: format!("Expected an intersect path with a path, found `{:?}`", intersect_path),
@ -831,10 +833,10 @@ async fn inner_angled_line_that_intersects(
}
/// Start a sketch at a given point.
pub async fn start_sketch_at(args: Args) -> Result<KclValue, KclError> {
pub async fn start_sketch_at(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let data: [f64; 2] = args.get_data()?;
let sketch_group = inner_start_sketch_at(data, args).await?;
let sketch_group = inner_start_sketch_at(data, exec_state, args).await?;
Ok(KclValue::new_user_val(sketch_group.meta.clone(), sketch_group))
}
@ -872,11 +874,15 @@ pub async fn start_sketch_at(args: Args) -> Result<KclValue, KclError> {
#[stdlib {
name = "startSketchAt",
}]
async fn inner_start_sketch_at(data: [f64; 2], args: Args) -> Result<SketchGroup, KclError> {
async fn inner_start_sketch_at(
data: [f64; 2],
exec_state: &mut ExecState,
args: Args,
) -> Result<SketchGroup, KclError> {
// Let's assume it's the XY plane for now, this is just for backwards compatibility.
let xy_plane = PlaneData::XY;
let sketch_surface = inner_start_sketch_on(SketchData::Plane(xy_plane), None, args.clone()).await?;
let sketch_group = inner_start_profile_at(data, sketch_surface, None, args).await?;
let sketch_surface = inner_start_sketch_on(SketchData::Plane(xy_plane), None, exec_state, &args).await?;
let sketch_group = inner_start_profile_at(data, sketch_surface, None, exec_state, args).await?;
Ok(sketch_group)
}
@ -1006,10 +1012,10 @@ impl From<PlaneData> for Plane {
}
/// Start a sketch on a specific plane or face.
pub async fn start_sketch_on(args: Args) -> Result<KclValue, KclError> {
pub async fn start_sketch_on(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (data, tag): (SketchData, Option<FaceTag>) = args.get_data_and_optional_tag()?;
match inner_start_sketch_on(data, tag, args).await? {
match inner_start_sketch_on(data, tag, exec_state, &args).await? {
SketchSurface::Plane(plane) => Ok(KclValue::Plane(plane)),
SketchSurface::Face(face) => Ok(KclValue::Face(face)),
}
@ -1119,7 +1125,12 @@ pub async fn start_sketch_on(args: Args) -> Result<KclValue, KclError> {
#[stdlib {
name = "startSketchOn",
}]
async fn inner_start_sketch_on(data: SketchData, tag: Option<FaceTag>, args: Args) -> Result<SketchSurface, KclError> {
async fn inner_start_sketch_on(
data: SketchData,
tag: Option<FaceTag>,
exec_state: &mut ExecState,
args: &Args,
) -> Result<SketchSurface, KclError> {
match data {
SketchData::Plane(plane_data) => {
let plane = start_sketch_on_plane(plane_data, args).await?;
@ -1132,7 +1143,7 @@ async fn inner_start_sketch_on(data: SketchData, tag: Option<FaceTag>, args: Arg
source_ranges: vec![args.source_range],
}));
};
let face = start_sketch_on_face(extrude_group, tag, args).await?;
let face = start_sketch_on_face(extrude_group, tag, exec_state, args).await?;
Ok(SketchSurface::Face(face))
}
}
@ -1141,9 +1152,10 @@ async fn inner_start_sketch_on(data: SketchData, tag: Option<FaceTag>, args: Arg
async fn start_sketch_on_face(
extrude_group: Box<ExtrudeGroup>,
tag: FaceTag,
args: Args,
exec_state: &mut ExecState,
args: &Args,
) -> Result<Box<Face>, KclError> {
let extrude_plane_id = tag.get_face_id(&extrude_group, &args, true).await?;
let extrude_plane_id = tag.get_face_id(&extrude_group, exec_state, args, true).await?;
Ok(Box::new(Face {
id: extrude_plane_id,
@ -1157,7 +1169,7 @@ async fn start_sketch_on_face(
}))
}
async fn start_sketch_on_plane(data: PlaneData, args: Args) -> Result<Box<Plane>, KclError> {
async fn start_sketch_on_plane(data: PlaneData, args: &Args) -> Result<Box<Plane>, KclError> {
let mut plane: Plane = data.clone().into();
// Get the default planes.
@ -1199,11 +1211,11 @@ async fn start_sketch_on_plane(data: PlaneData, args: Args) -> Result<Box<Plane>
}
/// Start a new profile at a given point.
pub async fn start_profile_at(args: Args) -> Result<KclValue, KclError> {
pub async fn start_profile_at(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (start, sketch_surface, tag): ([f64; 2], SketchSurface, Option<TagDeclarator>) =
args.get_data_and_sketch_surface()?;
let sketch_group = inner_start_profile_at(start, sketch_surface, tag, args).await?;
let sketch_group = inner_start_profile_at(start, sketch_surface, tag, exec_state, args).await?;
Ok(KclValue::new_user_val(sketch_group.meta.clone(), sketch_group))
}
@ -1248,12 +1260,13 @@ pub(crate) async fn inner_start_profile_at(
to: [f64; 2],
sketch_surface: SketchSurface,
tag: Option<TagDeclarator>,
exec_state: &mut ExecState,
args: Args,
) -> Result<SketchGroup, KclError> {
if let SketchSurface::Face(face) = &sketch_surface {
// Flush the batch for our fillets/chamfers if there are any.
// If we do not do these for sketch on face, things will fail with face does not exist.
args.flush_batch_for_extrude_group_set(face.extrude_group.clone().into())
args.flush_batch_for_extrude_group_set(exec_state, face.extrude_group.clone().into())
.await?;
}
@ -1328,7 +1341,7 @@ pub(crate) async fn inner_start_profile_at(
}
/// Returns the X component of the sketch profile start point.
pub async fn profile_start_x(args: Args) -> Result<KclValue, KclError> {
pub async fn profile_start_x(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let sketch_group: SketchGroup = args.get_sketch_group()?;
let x = inner_profile_start_x(sketch_group)?;
args.make_user_val_from_f64(x)
@ -1352,7 +1365,7 @@ pub(crate) fn inner_profile_start_x(sketch_group: SketchGroup) -> Result<f64, Kc
}
/// Returns the Y component of the sketch profile start point.
pub async fn profile_start_y(args: Args) -> Result<KclValue, KclError> {
pub async fn profile_start_y(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let sketch_group: SketchGroup = args.get_sketch_group()?;
let x = inner_profile_start_y(sketch_group)?;
args.make_user_val_from_f64(x)
@ -1375,7 +1388,7 @@ pub(crate) fn inner_profile_start_y(sketch_group: SketchGroup) -> Result<f64, Kc
}
/// Returns the sketch profile start point.
pub async fn profile_start(args: Args) -> Result<KclValue, KclError> {
pub async fn profile_start(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let sketch_group: SketchGroup = args.get_sketch_group()?;
let point = inner_profile_start(sketch_group)?;
Ok(KclValue::UserVal(UserVal {
@ -1409,7 +1422,7 @@ pub(crate) fn inner_profile_start(sketch_group: SketchGroup) -> Result<[f64; 2],
}
/// Close the current sketch.
pub async fn close(args: Args) -> Result<KclValue, KclError> {
pub async fn close(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (sketch_group, tag): (SketchGroup, Option<TagDeclarator>) = args.get_sketch_group_and_optional_tag()?;
let new_sketch_group = inner_close(sketch_group, tag, args).await?;
@ -1516,7 +1529,7 @@ pub enum ArcData {
}
/// Draw an arc.
pub async fn arc(args: Args) -> Result<KclValue, KclError> {
pub async fn arc(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (data, sketch_group, tag): (ArcData, SketchGroup, Option<TagDeclarator>) =
args.get_data_and_sketch_group_and_tag()?;
@ -1637,7 +1650,7 @@ pub enum TangentialArcData {
}
/// Draw a tangential arc.
pub async fn tangential_arc(args: Args) -> Result<KclValue, KclError> {
pub async fn tangential_arc(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (data, sketch_group, tag): (TangentialArcData, SketchGroup, Option<TagDeclarator>) =
args.get_data_and_sketch_group_and_tag()?;
@ -1767,7 +1780,7 @@ fn tan_arc_to(sketch_group: &SketchGroup, to: &[f64; 2]) -> ModelingCmd {
}
/// Draw a tangential arc to a specific point.
pub async fn tangential_arc_to(args: Args) -> Result<KclValue, KclError> {
pub async fn tangential_arc_to(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (to, sketch_group, tag): ([f64; 2], SketchGroup, Option<TagDeclarator>) =
super::args::FromArgs::from_args(&args, 0)?;
@ -1776,7 +1789,7 @@ pub async fn tangential_arc_to(args: Args) -> Result<KclValue, KclError> {
}
/// Draw a tangential arc to point some distance away..
pub async fn tangential_arc_to_relative(args: Args) -> Result<KclValue, KclError> {
pub async fn tangential_arc_to_relative(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (delta, sketch_group, tag): ([f64; 2], SketchGroup, Option<TagDeclarator>) =
super::args::FromArgs::from_args(&args, 0)?;
@ -1951,7 +1964,7 @@ pub struct BezierData {
}
/// Draw a bezier curve.
pub async fn bezier_curve(args: Args) -> Result<KclValue, KclError> {
pub async fn bezier_curve(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (data, sketch_group, tag): (BezierData, SketchGroup, Option<TagDeclarator>) =
args.get_data_and_sketch_group_and_tag()?;
@ -2043,7 +2056,7 @@ async fn inner_bezier_curve(
}
/// Use a sketch to cut a hole in another sketch.
pub async fn hole(args: Args) -> Result<KclValue, KclError> {
pub async fn hole(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (hole_sketch_group, sketch_group): (SketchGroupSet, SketchGroup) = args.get_sketch_groups()?;
let new_sketch_group = inner_hole(hole_sketch_group, sketch_group, args).await?;

View File

@ -4,10 +4,15 @@ use anyhow::Result;
use derive_docs::stdlib;
use schemars::JsonSchema;
use crate::{errors::KclError, executor::KclValue, settings::types::UnitLength, std::Args};
use crate::{
errors::KclError,
executor::{ExecState, KclValue},
settings::types::UnitLength,
std::Args,
};
/// Millimeters conversion factor for current projects units.
pub async fn mm(args: Args) -> Result<KclValue, KclError> {
pub async fn mm(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let result = inner_mm(&args)?;
args.make_user_val_from_f64(result)
@ -48,7 +53,7 @@ fn inner_mm(args: &Args) -> Result<f64, KclError> {
}
/// Inches conversion factor for current projects units.
pub async fn inch(args: Args) -> Result<KclValue, KclError> {
pub async fn inch(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let result = inner_inch(&args)?;
args.make_user_val_from_f64(result)
@ -89,7 +94,7 @@ fn inner_inch(args: &Args) -> Result<f64, KclError> {
}
/// Feet conversion factor for current projects units.
pub async fn ft(args: Args) -> Result<KclValue, KclError> {
pub async fn ft(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let result = inner_ft(&args)?;
args.make_user_val_from_f64(result)
@ -131,7 +136,7 @@ fn inner_ft(args: &Args) -> Result<f64, KclError> {
}
/// Meters conversion factor for current projects units.
pub async fn m(args: Args) -> Result<KclValue, KclError> {
pub async fn m(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let result = inner_m(&args)?;
args.make_user_val_from_f64(result)
@ -173,7 +178,7 @@ fn inner_m(args: &Args) -> Result<f64, KclError> {
}
/// Centimeters conversion factor for current projects units.
pub async fn cm(args: Args) -> Result<KclValue, KclError> {
pub async fn cm(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let result = inner_cm(&args)?;
args.make_user_val_from_f64(result)
@ -215,7 +220,7 @@ fn inner_cm(args: &Args) -> Result<f64, KclError> {
}
/// Yards conversion factor for current projects units.
pub async fn yd(args: Args) -> Result<KclValue, KclError> {
pub async fn yd(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let result = inner_yd(&args)?;
args.make_user_val_from_f64(result)

View File

@ -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

View File

@ -0,0 +1,7 @@
// Make sure pipe value doesn't leak into the function call.
fn f = (ignored) => {
return %
}
const answer = %
|> f(%)

View File

@ -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);

View File

@ -35,12 +35,12 @@ async fn setup(code: &str, name: &str) -> Result<(ExecutorContext, Program, uuid
let parser = kcl_lib::parser::Parser::new(tokens);
let program = parser.ast()?;
let ctx = kcl_lib::executor::ExecutorContext::new(&client, Default::default()).await?;
let memory = ctx.run(&program, None).await?;
let exec_state = ctx.run(&program, None).await?;
// We need to get the sketch ID.
// Get the sketch group ID from memory.
let KclValue::UserVal(user_val) = memory.get(name, SourceRange::default()).unwrap() else {
anyhow::bail!("part001 not found in memory: {:?}", memory);
let KclValue::UserVal(user_val) = exec_state.memory.get(name, SourceRange::default()).unwrap() else {
anyhow::bail!("part001 not found in memory: {:?}", exec_state.memory);
};
let Some((sketch_group, _meta)) = user_val.get::<SketchGroup>() else {
anyhow::bail!("part001 was not a SketchGroup");