KCL: Reduce can take and return any KCL values (#4094)
Previously it only took Array of Number and could only return Sketch. Now it has been unshackled from the chains of poor type signatures.
This commit is contained in:
File diff suppressed because one or more lines are too long
13573
docs/kcl/std.json
13573
docs/kcl/std.json
File diff suppressed because it is too large
Load Diff
@ -787,12 +787,14 @@ fn test_generate_stdlib_json_schema() {
|
|||||||
let stdlib = StdLib::new();
|
let stdlib = StdLib::new();
|
||||||
let combined = stdlib.combined();
|
let combined = stdlib.combined();
|
||||||
|
|
||||||
let mut json_data = vec![];
|
let json_data: Vec<_> = combined
|
||||||
|
.keys()
|
||||||
for key in combined.keys().sorted() {
|
.sorted()
|
||||||
|
.map(|key| {
|
||||||
let internal_fn = combined.get(key).unwrap();
|
let internal_fn = combined.get(key).unwrap();
|
||||||
json_data.push(internal_fn.to_json().unwrap());
|
internal_fn.to_json().unwrap()
|
||||||
}
|
})
|
||||||
|
.collect();
|
||||||
expectorate::assert_contents(
|
expectorate::assert_contents(
|
||||||
"../../../docs/kcl/std.json",
|
"../../../docs/kcl/std.json",
|
||||||
&serde_json::to_string_pretty(&json_data).unwrap(),
|
&serde_json::to_string_pretty(&json_data).unwrap(),
|
||||||
|
@ -83,6 +83,8 @@ impl StdLibFnArg {
|
|||||||
return Ok(Some((index, format!("${{{}:{}}}", index, "myTag"))));
|
return Ok(Some((index, format!("${{{}:{}}}", index, "myTag"))));
|
||||||
} else if self.type_ == "[KclValue]" && self.required {
|
} else if self.type_ == "[KclValue]" && self.required {
|
||||||
return Ok(Some((index, format!("${{{}:{}}}", index, "[0..9]"))));
|
return Ok(Some((index, format!("${{{}:{}}}", index, "[0..9]"))));
|
||||||
|
} else if self.type_ == "KclValue" && self.required {
|
||||||
|
return Ok(Some((index, format!("${{{}:{}}}", index, "3"))));
|
||||||
}
|
}
|
||||||
get_autocomplete_snippet_from_schema(&self.schema.schema.clone().into(), index)
|
get_autocomplete_snippet_from_schema(&self.schema.schema.clone().into(), index)
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ use serde_json::Value as JValue;
|
|||||||
use super::{args::FromArgs, Args, FnAsArg};
|
use super::{args::FromArgs, Args, FnAsArg};
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
executor::{ExecState, KclValue, Sketch, SourceRange, UserVal},
|
executor::{ExecState, KclValue, SourceRange, UserVal},
|
||||||
function_param::FunctionParam,
|
function_param::FunctionParam,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -98,7 +98,16 @@ async fn call_map_closure<'a>(
|
|||||||
|
|
||||||
/// For each item in an array, update a value.
|
/// For each item in an array, update a value.
|
||||||
pub async fn reduce(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn reduce(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let (array, start, f): (Vec<u64>, Sketch, FnAsArg<'_>) = FromArgs::from_args(&args, 0)?;
|
let (array, start, f): (Vec<JValue>, KclValue, FnAsArg<'_>) = FromArgs::from_args(&args, 0)?;
|
||||||
|
let array: Vec<KclValue> = array
|
||||||
|
.into_iter()
|
||||||
|
.map(|jval| {
|
||||||
|
KclValue::UserVal(UserVal {
|
||||||
|
value: jval,
|
||||||
|
meta: vec![args.source_range.into()],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
let reduce_fn = FunctionParam {
|
let reduce_fn = FunctionParam {
|
||||||
inner: f.func,
|
inner: f.func,
|
||||||
fn_expr: f.expr,
|
fn_expr: f.expr,
|
||||||
@ -106,9 +115,7 @@ pub async fn reduce(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
|||||||
ctx: args.ctx.clone(),
|
ctx: args.ctx.clone(),
|
||||||
memory: *f.memory,
|
memory: *f.memory,
|
||||||
};
|
};
|
||||||
inner_reduce(array, start, reduce_fn, exec_state, &args)
|
inner_reduce(array, start, reduce_fn, exec_state, &args).await
|
||||||
.await
|
|
||||||
.map(|sg| KclValue::UserVal(UserVal::new(sg.meta.clone(), sg)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Take a starting value. Then, for each element of an array, calculate the next value,
|
/// Take a starting value. Then, for each element of an array, calculate the next value,
|
||||||
@ -125,60 +132,52 @@ pub async fn reduce(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
|||||||
/// }
|
/// }
|
||||||
/// decagon(5.0) |> close(%)
|
/// decagon(5.0) |> close(%)
|
||||||
/// ```
|
/// ```
|
||||||
|
/// ```no_run
|
||||||
|
/// array = [1, 2, 3]
|
||||||
|
/// sum = reduce(array, 0, (i, result_so_far) => { return i + result_so_far })
|
||||||
|
/// assertEqual(sum, 6, 0.00001, "1 + 2 + 3 summed is 6")
|
||||||
|
/// ```
|
||||||
|
/// ```no_run
|
||||||
|
/// fn add = (a, b) => { return a + b }
|
||||||
|
/// fn sum = (array) => { return reduce(array, 0, add) }
|
||||||
|
/// assertEqual(sum([1, 2, 3]), 6, 0.00001, "1 + 2 + 3 summed is 6")
|
||||||
|
/// ```
|
||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "reduce",
|
name = "reduce",
|
||||||
}]
|
}]
|
||||||
async fn inner_reduce<'a>(
|
async fn inner_reduce<'a>(
|
||||||
array: Vec<u64>,
|
array: Vec<KclValue>,
|
||||||
start: Sketch,
|
start: KclValue,
|
||||||
reduce_fn: FunctionParam<'a>,
|
reduce_fn: FunctionParam<'a>,
|
||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
args: &'a Args,
|
args: &'a Args,
|
||||||
) -> Result<Sketch, KclError> {
|
) -> Result<KclValue, KclError> {
|
||||||
let mut reduced = start;
|
let mut reduced = start;
|
||||||
for i in array {
|
for elem in array {
|
||||||
reduced = call_reduce_closure(i, reduced, &reduce_fn, args.source_range, exec_state).await?;
|
reduced = call_reduce_closure(elem, reduced, &reduce_fn, args.source_range, exec_state).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(reduced)
|
Ok(reduced)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn call_reduce_closure<'a>(
|
async fn call_reduce_closure<'a>(
|
||||||
i: u64,
|
elem: KclValue,
|
||||||
start: Sketch,
|
start: KclValue,
|
||||||
reduce_fn: &FunctionParam<'a>,
|
reduce_fn: &FunctionParam<'a>,
|
||||||
source_range: SourceRange,
|
source_range: SourceRange,
|
||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
) -> Result<Sketch, KclError> {
|
) -> Result<KclValue, KclError> {
|
||||||
// Call the reduce fn for this repetition.
|
// Call the reduce fn for this repetition.
|
||||||
let reduce_fn_args = vec![
|
let reduce_fn_args = vec![elem, start];
|
||||||
KclValue::UserVal(UserVal {
|
|
||||||
value: serde_json::Value::Number(i.into()),
|
|
||||||
meta: vec![source_range.into()],
|
|
||||||
}),
|
|
||||||
KclValue::new_user_val(start.meta.clone(), start),
|
|
||||||
];
|
|
||||||
let transform_fn_return = reduce_fn.call(exec_state, reduce_fn_args).await?;
|
let transform_fn_return = reduce_fn.call(exec_state, reduce_fn_args).await?;
|
||||||
|
|
||||||
// Unpack the returned transform object.
|
// Unpack the returned transform object.
|
||||||
let source_ranges = vec![source_range];
|
let source_ranges = vec![source_range];
|
||||||
let closure_retval = transform_fn_return.ok_or_else(|| {
|
let out = transform_fn_return.ok_or_else(|| {
|
||||||
KclError::Semantic(KclErrorDetails {
|
KclError::Semantic(KclErrorDetails {
|
||||||
message: "Reducer function must return a value".to_string(),
|
message: "Reducer function must return a value".to_string(),
|
||||||
source_ranges: source_ranges.clone(),
|
source_ranges: source_ranges.clone(),
|
||||||
})
|
})
|
||||||
})?;
|
})?;
|
||||||
let Some(out) = closure_retval.as_user_val() else {
|
|
||||||
return Err(KclError::Semantic(KclErrorDetails {
|
|
||||||
message: "Reducer function must return a UserValue".to_string(),
|
|
||||||
source_ranges: source_ranges.clone(),
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
let Some((out, _meta)) = out.get() else {
|
|
||||||
return Err(KclError::Semantic(KclErrorDetails {
|
|
||||||
message: "Reducer function must return a Sketch".to_string(),
|
|
||||||
source_ranges: source_ranges.clone(),
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
Ok(out)
|
Ok(out)
|
||||||
}
|
}
|
||||||
|
BIN
src/wasm-lib/kcl/tests/outputs/serial_test_example_reduce1.png
Normal file
BIN
src/wasm-lib/kcl/tests/outputs/serial_test_example_reduce1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 19 KiB |
BIN
src/wasm-lib/kcl/tests/outputs/serial_test_example_reduce2.png
Normal file
BIN
src/wasm-lib/kcl/tests/outputs/serial_test_example_reduce2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 19 KiB |
Reference in New Issue
Block a user