KCL keyword args: calling user-defined functions (#4722)
Part of https://github.com/KittyCAD/modeling-app/issues/4600 You can now call a user-defined function via keyword args. E.g. ``` fn increment(@x) { return x + 1 } fn add(@x, delta) { return x + delta } two = increment(1) three = add(1, delta: 2) ```
This commit is contained in:
@ -366,6 +366,7 @@ impl Node<CallExpressionKw> {
|
|||||||
#[async_recursion]
|
#[async_recursion]
|
||||||
pub async fn execute(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
|
pub async fn execute(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
|
||||||
let fn_name = &self.callee.name;
|
let fn_name = &self.callee.name;
|
||||||
|
let callsite: SourceRange = self.into();
|
||||||
|
|
||||||
// Build a hashmap from argument labels to the final evaluated values.
|
// Build a hashmap from argument labels to the final evaluated values.
|
||||||
let mut fn_args = HashMap::with_capacity(self.arguments.len());
|
let mut fn_args = HashMap::with_capacity(self.arguments.len());
|
||||||
@ -412,7 +413,39 @@ impl Node<CallExpressionKw> {
|
|||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
FunctionKind::UserDefined => {
|
FunctionKind::UserDefined => {
|
||||||
todo!("Part of modeling-app#4600: Support keyword arguments for user-defined functions")
|
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 return_value = {
|
||||||
|
let previous_dynamic_state = std::mem::replace(&mut exec_state.dynamic_state, fn_dynamic_state);
|
||||||
|
let result = func
|
||||||
|
.call_fn_kw(args, exec_state, ctx.clone(), callsite)
|
||||||
|
.await
|
||||||
|
.map_err(|e| {
|
||||||
|
// Add the call expression to the source ranges.
|
||||||
|
// TODO currently ignored by the frontend
|
||||||
|
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();
|
||||||
|
};
|
||||||
|
KclError::UndefinedValue(KclErrorDetails {
|
||||||
|
message: format!("Result of user-defined function {} is undefined", fn_name),
|
||||||
|
source_ranges,
|
||||||
|
})
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -72,6 +72,10 @@ pub enum KclValue {
|
|||||||
ImportedGeometry(ImportedGeometry),
|
ImportedGeometry(ImportedGeometry),
|
||||||
#[ts(skip)]
|
#[ts(skip)]
|
||||||
Function {
|
Function {
|
||||||
|
/// Adam Chalmers speculation:
|
||||||
|
/// Reference to a KCL stdlib function (written in Rust).
|
||||||
|
/// Some if the KCL value is an alias of a stdlib function,
|
||||||
|
/// None if it's a KCL function written/declared in KCL.
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
func: Option<MemoryFunction>,
|
func: Option<MemoryFunction>,
|
||||||
#[schemars(skip)]
|
#[schemars(skip)]
|
||||||
@ -503,4 +507,39 @@ impl KclValue {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// If this is a function, call it by applying keyword arguments.
|
||||||
|
/// If it's not a function, returns an error.
|
||||||
|
pub async fn call_fn_kw(
|
||||||
|
&self,
|
||||||
|
args: crate::std::Args,
|
||||||
|
exec_state: &mut ExecState,
|
||||||
|
ctx: ExecutorContext,
|
||||||
|
callsite: SourceRange,
|
||||||
|
) -> Result<Option<KclValue>, KclError> {
|
||||||
|
let KclValue::Function {
|
||||||
|
func,
|
||||||
|
expression,
|
||||||
|
memory: closure_memory,
|
||||||
|
meta: _,
|
||||||
|
} = &self
|
||||||
|
else {
|
||||||
|
return Err(KclError::Semantic(KclErrorDetails {
|
||||||
|
message: "cannot call this because it isn't a function".to_string(),
|
||||||
|
source_ranges: vec![callsite],
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
if let Some(_func) = func {
|
||||||
|
todo!("Implement calling KCL stdlib fns that are aliased. Part of https://github.com/KittyCAD/modeling-app/issues/4600");
|
||||||
|
} else {
|
||||||
|
crate::execution::call_user_defined_function_kw(
|
||||||
|
args.kw_args,
|
||||||
|
closure_memory.as_ref(),
|
||||||
|
expression.as_ref(),
|
||||||
|
exec_state,
|
||||||
|
&ctx,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2285,6 +2285,59 @@ fn assign_args_to_params(
|
|||||||
Ok(fn_memory)
|
Ok(fn_memory)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn assign_args_to_params_kw(
|
||||||
|
function_expression: NodeRef<'_, FunctionExpression>,
|
||||||
|
mut args: crate::std::args::KwArgs,
|
||||||
|
mut fn_memory: ProgramMemory,
|
||||||
|
) -> Result<ProgramMemory, KclError> {
|
||||||
|
// Add the arguments to the memory. A new call frame should have already
|
||||||
|
// been created.
|
||||||
|
let source_ranges = vec![function_expression.into()];
|
||||||
|
for param in function_expression.params.iter() {
|
||||||
|
if param.labeled {
|
||||||
|
let arg = args.labeled.get(¶m.identifier.name);
|
||||||
|
let arg_val = match arg {
|
||||||
|
Some(arg) => arg.value.clone(),
|
||||||
|
None => match param.default_value {
|
||||||
|
Some(ref default_val) => KclValue::from(default_val.clone()),
|
||||||
|
None => {
|
||||||
|
return Err(KclError::Semantic(KclErrorDetails {
|
||||||
|
source_ranges,
|
||||||
|
message: format!(
|
||||||
|
"This function requires a parameter {}, but you haven't passed it one.",
|
||||||
|
param.identifier.name
|
||||||
|
),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
fn_memory.add(¶m.identifier.name, arg_val, (¶m.identifier).into())?;
|
||||||
|
} else {
|
||||||
|
let Some(unlabeled) = args.unlabeled.take() else {
|
||||||
|
let param_name = ¶m.identifier.name;
|
||||||
|
return Err(if args.labeled.contains_key(param_name) {
|
||||||
|
KclError::Semantic(KclErrorDetails {
|
||||||
|
source_ranges,
|
||||||
|
message: format!("The function does declare a parameter named '{param_name}', but this parameter doesn't use a label. Try removing the `{param_name}:`"),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
KclError::Semantic(KclErrorDetails {
|
||||||
|
source_ranges,
|
||||||
|
message: "This function expects an unlabeled first parameter, but you haven't passed it one."
|
||||||
|
.to_owned(),
|
||||||
|
})
|
||||||
|
});
|
||||||
|
};
|
||||||
|
fn_memory.add(
|
||||||
|
¶m.identifier.name,
|
||||||
|
unlabeled.value.clone(),
|
||||||
|
(¶m.identifier).into(),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(fn_memory)
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) async fn call_user_defined_function(
|
pub(crate) async fn call_user_defined_function(
|
||||||
args: Vec<Arg>,
|
args: Vec<Arg>,
|
||||||
memory: &ProgramMemory,
|
memory: &ProgramMemory,
|
||||||
@ -2315,6 +2368,36 @@ pub(crate) async fn call_user_defined_function(
|
|||||||
result.map(|_| fn_memory.return_)
|
result.map(|_| fn_memory.return_)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn call_user_defined_function_kw(
|
||||||
|
args: crate::std::args::KwArgs,
|
||||||
|
memory: &ProgramMemory,
|
||||||
|
function_expression: NodeRef<'_, 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_kw(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> {
|
pub enum StatementKind<'a> {
|
||||||
Declaration { name: &'a str },
|
Declaration { name: &'a str },
|
||||||
Expression,
|
Expression,
|
||||||
|
|||||||
@ -1544,3 +1544,45 @@ mod tag_proxied_through_function_does_not_define_var {
|
|||||||
super::execute(TEST_NAME, false).await
|
super::execute(TEST_NAME, false).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mod kw_fn_too_few_args {
|
||||||
|
const TEST_NAME: &str = "kw_fn_too_few_args";
|
||||||
|
|
||||||
|
/// Test parsing KCL.
|
||||||
|
#[test]
|
||||||
|
fn parse() {
|
||||||
|
super::parse(TEST_NAME)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test that parsing and unparsing KCL produces the original KCL input.
|
||||||
|
#[test]
|
||||||
|
fn unparse() {
|
||||||
|
super::unparse(TEST_NAME)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test that KCL is executed correctly.
|
||||||
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
async fn kcl_test_execute() {
|
||||||
|
super::execute(TEST_NAME, true).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mod kw_fn_unlabeled_but_has_label {
|
||||||
|
const TEST_NAME: &str = "kw_fn_unlabeled_but_has_label";
|
||||||
|
|
||||||
|
/// Test parsing KCL.
|
||||||
|
#[test]
|
||||||
|
fn parse() {
|
||||||
|
super::parse(TEST_NAME)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test that parsing and unparsing KCL produces the original KCL input.
|
||||||
|
#[test]
|
||||||
|
fn unparse() {
|
||||||
|
super::unparse(TEST_NAME)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test that KCL is executed correctly.
|
||||||
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
async fn kcl_test_execute() {
|
||||||
|
super::execute(TEST_NAME, true).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -50,6 +50,13 @@ pub struct KwArgs {
|
|||||||
pub labeled: HashMap<String, Arg>,
|
pub labeled: HashMap<String, Arg>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl KwArgs {
|
||||||
|
/// How many arguments are there?
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.labeled.len() + if self.unlabeled.is_some() { 1 } else { 0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Args {
|
pub struct Args {
|
||||||
/// Positional args.
|
/// Positional args.
|
||||||
|
|||||||
@ -78,46 +78,179 @@ snapshot_kind: text
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"declaration": {
|
"declaration": {
|
||||||
"end": 55,
|
"end": 77,
|
||||||
"id": {
|
"id": {
|
||||||
"end": 40,
|
"end": 43,
|
||||||
|
"name": "add",
|
||||||
|
"start": 40,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"init": {
|
||||||
|
"body": {
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"argument": {
|
||||||
|
"end": 75,
|
||||||
|
"left": {
|
||||||
|
"end": 67,
|
||||||
|
"name": "x",
|
||||||
|
"start": 66,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"operator": "+",
|
||||||
|
"right": {
|
||||||
|
"end": 75,
|
||||||
|
"name": "delta",
|
||||||
|
"start": 70,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"start": 66,
|
||||||
|
"type": "BinaryExpression",
|
||||||
|
"type": "BinaryExpression"
|
||||||
|
},
|
||||||
|
"end": 75,
|
||||||
|
"start": 59,
|
||||||
|
"type": "ReturnStatement",
|
||||||
|
"type": "ReturnStatement"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"end": 77,
|
||||||
|
"start": 55
|
||||||
|
},
|
||||||
|
"end": 77,
|
||||||
|
"params": [
|
||||||
|
{
|
||||||
|
"type": "Parameter",
|
||||||
|
"identifier": {
|
||||||
|
"end": 46,
|
||||||
|
"name": "x",
|
||||||
|
"start": 45,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"labeled": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Parameter",
|
||||||
|
"identifier": {
|
||||||
|
"end": 53,
|
||||||
|
"name": "delta",
|
||||||
|
"start": 48,
|
||||||
|
"type": "Identifier"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"start": 43,
|
||||||
|
"type": "FunctionExpression",
|
||||||
|
"type": "FunctionExpression"
|
||||||
|
},
|
||||||
|
"start": 40,
|
||||||
|
"type": "VariableDeclarator"
|
||||||
|
},
|
||||||
|
"end": 77,
|
||||||
|
"kind": "fn",
|
||||||
|
"start": 37,
|
||||||
|
"type": "VariableDeclaration",
|
||||||
|
"type": "VariableDeclaration"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"declaration": {
|
||||||
|
"end": 97,
|
||||||
|
"id": {
|
||||||
|
"end": 82,
|
||||||
"name": "two",
|
"name": "two",
|
||||||
"start": 37,
|
"start": 79,
|
||||||
"type": "Identifier"
|
"type": "Identifier"
|
||||||
},
|
},
|
||||||
"init": {
|
"init": {
|
||||||
"arguments": [
|
"arguments": [
|
||||||
{
|
{
|
||||||
"end": 54,
|
"end": 96,
|
||||||
"raw": "1",
|
"raw": "1",
|
||||||
"start": 53,
|
"start": 95,
|
||||||
"type": "Literal",
|
"type": "Literal",
|
||||||
"type": "Literal",
|
"type": "Literal",
|
||||||
"value": 1.0
|
"value": 1.0
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"callee": {
|
"callee": {
|
||||||
"end": 52,
|
"end": 94,
|
||||||
"name": "increment",
|
"name": "increment",
|
||||||
"start": 43,
|
"start": 85,
|
||||||
"type": "Identifier"
|
"type": "Identifier"
|
||||||
},
|
},
|
||||||
"end": 55,
|
"end": 97,
|
||||||
"start": 43,
|
"start": 85,
|
||||||
"type": "CallExpression",
|
"type": "CallExpression",
|
||||||
"type": "CallExpression"
|
"type": "CallExpression"
|
||||||
},
|
},
|
||||||
"start": 37,
|
"start": 79,
|
||||||
"type": "VariableDeclarator"
|
"type": "VariableDeclarator"
|
||||||
},
|
},
|
||||||
"end": 55,
|
"end": 97,
|
||||||
"kind": "const",
|
"kind": "const",
|
||||||
"start": 37,
|
"start": 79,
|
||||||
|
"type": "VariableDeclaration",
|
||||||
|
"type": "VariableDeclaration"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"declaration": {
|
||||||
|
"end": 122,
|
||||||
|
"id": {
|
||||||
|
"end": 103,
|
||||||
|
"name": "three",
|
||||||
|
"start": 98,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"init": {
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"type": "LabeledArg",
|
||||||
|
"label": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"name": "delta"
|
||||||
|
},
|
||||||
|
"arg": {
|
||||||
|
"end": 121,
|
||||||
|
"raw": "2",
|
||||||
|
"start": 120,
|
||||||
|
"type": "Literal",
|
||||||
|
"type": "Literal",
|
||||||
|
"value": 2.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"callee": {
|
||||||
|
"end": 109,
|
||||||
|
"name": "add",
|
||||||
|
"start": 106,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"end": 122,
|
||||||
|
"start": 106,
|
||||||
|
"type": "CallExpressionKw",
|
||||||
|
"type": "CallExpressionKw",
|
||||||
|
"unlabeled": {
|
||||||
|
"end": 111,
|
||||||
|
"raw": "1",
|
||||||
|
"start": 110,
|
||||||
|
"type": "Literal",
|
||||||
|
"type": "Literal",
|
||||||
|
"value": 1.0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"start": 98,
|
||||||
|
"type": "VariableDeclarator"
|
||||||
|
},
|
||||||
|
"end": 122,
|
||||||
|
"kind": "const",
|
||||||
|
"start": 98,
|
||||||
"type": "VariableDeclaration",
|
"type": "VariableDeclaration",
|
||||||
"type": "VariableDeclaration"
|
"type": "VariableDeclaration"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"end": 56,
|
"end": 123,
|
||||||
"nonCodeMeta": {
|
"nonCodeMeta": {
|
||||||
"nonCodeNodes": {
|
"nonCodeNodes": {
|
||||||
"0": [
|
"0": [
|
||||||
@ -129,6 +262,16 @@ snapshot_kind: text
|
|||||||
"type": "newLine"
|
"type": "newLine"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"1": [
|
||||||
|
{
|
||||||
|
"end": 79,
|
||||||
|
"start": 77,
|
||||||
|
"type": "NonCodeNode",
|
||||||
|
"value": {
|
||||||
|
"type": "newLine"
|
||||||
|
}
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"startNodes": []
|
"startNodes": []
|
||||||
|
|||||||
@ -2,4 +2,9 @@ fn increment(@x) {
|
|||||||
return x + 1
|
return x + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn add(@x, delta) {
|
||||||
|
return x + delta
|
||||||
|
}
|
||||||
|
|
||||||
two = increment(1)
|
two = increment(1)
|
||||||
|
three = add(1, delta: 2)
|
||||||
|
|||||||
@ -27,6 +27,202 @@ snapshot_kind: text
|
|||||||
"value": 0.0,
|
"value": 0.0,
|
||||||
"__meta": []
|
"__meta": []
|
||||||
},
|
},
|
||||||
|
"add": {
|
||||||
|
"type": "Function",
|
||||||
|
"expression": {
|
||||||
|
"body": {
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"argument": {
|
||||||
|
"end": 75,
|
||||||
|
"left": {
|
||||||
|
"end": 67,
|
||||||
|
"name": "x",
|
||||||
|
"start": 66,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"operator": "+",
|
||||||
|
"right": {
|
||||||
|
"end": 75,
|
||||||
|
"name": "delta",
|
||||||
|
"start": 70,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"start": 66,
|
||||||
|
"type": "BinaryExpression",
|
||||||
|
"type": "BinaryExpression"
|
||||||
|
},
|
||||||
|
"end": 75,
|
||||||
|
"start": 59,
|
||||||
|
"type": "ReturnStatement",
|
||||||
|
"type": "ReturnStatement"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"end": 77,
|
||||||
|
"start": 55
|
||||||
|
},
|
||||||
|
"end": 77,
|
||||||
|
"params": [
|
||||||
|
{
|
||||||
|
"type": "Parameter",
|
||||||
|
"identifier": {
|
||||||
|
"end": 46,
|
||||||
|
"name": "x",
|
||||||
|
"start": 45,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"labeled": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Parameter",
|
||||||
|
"identifier": {
|
||||||
|
"end": 53,
|
||||||
|
"name": "delta",
|
||||||
|
"start": 48,
|
||||||
|
"type": "Identifier"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"start": 43,
|
||||||
|
"type": "FunctionExpression"
|
||||||
|
},
|
||||||
|
"memory": {
|
||||||
|
"environments": [
|
||||||
|
{
|
||||||
|
"bindings": {
|
||||||
|
"HALF_TURN": {
|
||||||
|
"type": "Number",
|
||||||
|
"value": 180.0,
|
||||||
|
"__meta": []
|
||||||
|
},
|
||||||
|
"QUARTER_TURN": {
|
||||||
|
"type": "Number",
|
||||||
|
"value": 90.0,
|
||||||
|
"__meta": []
|
||||||
|
},
|
||||||
|
"THREE_QUARTER_TURN": {
|
||||||
|
"type": "Number",
|
||||||
|
"value": 270.0,
|
||||||
|
"__meta": []
|
||||||
|
},
|
||||||
|
"ZERO": {
|
||||||
|
"type": "Number",
|
||||||
|
"value": 0.0,
|
||||||
|
"__meta": []
|
||||||
|
},
|
||||||
|
"increment": {
|
||||||
|
"type": "Function",
|
||||||
|
"expression": {
|
||||||
|
"body": {
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"argument": {
|
||||||
|
"end": 33,
|
||||||
|
"left": {
|
||||||
|
"end": 29,
|
||||||
|
"name": "x",
|
||||||
|
"start": 28,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"operator": "+",
|
||||||
|
"right": {
|
||||||
|
"end": 33,
|
||||||
|
"raw": "1",
|
||||||
|
"start": 32,
|
||||||
|
"type": "Literal",
|
||||||
|
"type": "Literal",
|
||||||
|
"value": 1.0
|
||||||
|
},
|
||||||
|
"start": 28,
|
||||||
|
"type": "BinaryExpression",
|
||||||
|
"type": "BinaryExpression"
|
||||||
|
},
|
||||||
|
"end": 33,
|
||||||
|
"start": 21,
|
||||||
|
"type": "ReturnStatement",
|
||||||
|
"type": "ReturnStatement"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"end": 35,
|
||||||
|
"start": 17
|
||||||
|
},
|
||||||
|
"end": 35,
|
||||||
|
"params": [
|
||||||
|
{
|
||||||
|
"type": "Parameter",
|
||||||
|
"identifier": {
|
||||||
|
"end": 15,
|
||||||
|
"name": "x",
|
||||||
|
"start": 14,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"labeled": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"start": 12,
|
||||||
|
"type": "FunctionExpression"
|
||||||
|
},
|
||||||
|
"memory": {
|
||||||
|
"environments": [
|
||||||
|
{
|
||||||
|
"bindings": {
|
||||||
|
"HALF_TURN": {
|
||||||
|
"type": "Number",
|
||||||
|
"value": 180.0,
|
||||||
|
"__meta": []
|
||||||
|
},
|
||||||
|
"QUARTER_TURN": {
|
||||||
|
"type": "Number",
|
||||||
|
"value": 90.0,
|
||||||
|
"__meta": []
|
||||||
|
},
|
||||||
|
"THREE_QUARTER_TURN": {
|
||||||
|
"type": "Number",
|
||||||
|
"value": 270.0,
|
||||||
|
"__meta": []
|
||||||
|
},
|
||||||
|
"ZERO": {
|
||||||
|
"type": "Number",
|
||||||
|
"value": 0.0,
|
||||||
|
"__meta": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"parent": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"currentEnv": 0,
|
||||||
|
"return": null
|
||||||
|
},
|
||||||
|
"__meta": [
|
||||||
|
{
|
||||||
|
"sourceRange": [
|
||||||
|
12,
|
||||||
|
35,
|
||||||
|
0
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"parent": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"currentEnv": 0,
|
||||||
|
"return": null
|
||||||
|
},
|
||||||
|
"__meta": [
|
||||||
|
{
|
||||||
|
"sourceRange": [
|
||||||
|
43,
|
||||||
|
77,
|
||||||
|
0
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"increment": {
|
"increment": {
|
||||||
"type": "Function",
|
"type": "Function",
|
||||||
"expression": {
|
"expression": {
|
||||||
@ -121,14 +317,34 @@ snapshot_kind: text
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"three": {
|
||||||
|
"type": "Number",
|
||||||
|
"value": 3.0,
|
||||||
|
"__meta": [
|
||||||
|
{
|
||||||
|
"sourceRange": [
|
||||||
|
110,
|
||||||
|
111,
|
||||||
|
0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sourceRange": [
|
||||||
|
120,
|
||||||
|
121,
|
||||||
|
0
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"two": {
|
"two": {
|
||||||
"type": "Number",
|
"type": "Number",
|
||||||
"value": 2.0,
|
"value": 2.0,
|
||||||
"__meta": [
|
"__meta": [
|
||||||
{
|
{
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
53,
|
95,
|
||||||
54,
|
96,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
153
src/wasm-lib/kcl/tests/kw_fn_too_few_args/ast.snap
Normal file
153
src/wasm-lib/kcl/tests/kw_fn_too_few_args/ast.snap
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Result of parsing kw_fn_too_few_args.kcl
|
||||||
|
snapshot_kind: text
|
||||||
|
---
|
||||||
|
{
|
||||||
|
"Ok": {
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"declaration": {
|
||||||
|
"end": 31,
|
||||||
|
"id": {
|
||||||
|
"end": 6,
|
||||||
|
"name": "add",
|
||||||
|
"start": 3,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"init": {
|
||||||
|
"body": {
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"argument": {
|
||||||
|
"end": 29,
|
||||||
|
"left": {
|
||||||
|
"end": 25,
|
||||||
|
"name": "x",
|
||||||
|
"start": 24,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"operator": "+",
|
||||||
|
"right": {
|
||||||
|
"end": 29,
|
||||||
|
"name": "y",
|
||||||
|
"start": 28,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"start": 24,
|
||||||
|
"type": "BinaryExpression",
|
||||||
|
"type": "BinaryExpression"
|
||||||
|
},
|
||||||
|
"end": 29,
|
||||||
|
"start": 17,
|
||||||
|
"type": "ReturnStatement",
|
||||||
|
"type": "ReturnStatement"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"end": 31,
|
||||||
|
"start": 13
|
||||||
|
},
|
||||||
|
"end": 31,
|
||||||
|
"params": [
|
||||||
|
{
|
||||||
|
"type": "Parameter",
|
||||||
|
"identifier": {
|
||||||
|
"end": 8,
|
||||||
|
"name": "x",
|
||||||
|
"start": 7,
|
||||||
|
"type": "Identifier"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Parameter",
|
||||||
|
"identifier": {
|
||||||
|
"end": 11,
|
||||||
|
"name": "y",
|
||||||
|
"start": 10,
|
||||||
|
"type": "Identifier"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"start": 6,
|
||||||
|
"type": "FunctionExpression",
|
||||||
|
"type": "FunctionExpression"
|
||||||
|
},
|
||||||
|
"start": 3,
|
||||||
|
"type": "VariableDeclarator"
|
||||||
|
},
|
||||||
|
"end": 31,
|
||||||
|
"kind": "fn",
|
||||||
|
"start": 0,
|
||||||
|
"type": "VariableDeclaration",
|
||||||
|
"type": "VariableDeclaration"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"declaration": {
|
||||||
|
"end": 50,
|
||||||
|
"id": {
|
||||||
|
"end": 38,
|
||||||
|
"name": "three",
|
||||||
|
"start": 33,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"init": {
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"type": "LabeledArg",
|
||||||
|
"label": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"name": "x"
|
||||||
|
},
|
||||||
|
"arg": {
|
||||||
|
"end": 49,
|
||||||
|
"raw": "1",
|
||||||
|
"start": 48,
|
||||||
|
"type": "Literal",
|
||||||
|
"type": "Literal",
|
||||||
|
"value": 1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"callee": {
|
||||||
|
"end": 44,
|
||||||
|
"name": "add",
|
||||||
|
"start": 41,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"end": 50,
|
||||||
|
"start": 41,
|
||||||
|
"type": "CallExpressionKw",
|
||||||
|
"type": "CallExpressionKw",
|
||||||
|
"unlabeled": null
|
||||||
|
},
|
||||||
|
"start": 33,
|
||||||
|
"type": "VariableDeclarator"
|
||||||
|
},
|
||||||
|
"end": 50,
|
||||||
|
"kind": "const",
|
||||||
|
"start": 33,
|
||||||
|
"type": "VariableDeclaration",
|
||||||
|
"type": "VariableDeclaration"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"end": 51,
|
||||||
|
"nonCodeMeta": {
|
||||||
|
"nonCodeNodes": {
|
||||||
|
"0": [
|
||||||
|
{
|
||||||
|
"end": 33,
|
||||||
|
"start": 31,
|
||||||
|
"type": "NonCodeNode",
|
||||||
|
"value": {
|
||||||
|
"type": "newLine"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"startNodes": []
|
||||||
|
},
|
||||||
|
"start": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Error from executing kw_fn_too_few_args.kcl
|
||||||
|
snapshot_kind: text
|
||||||
|
---
|
||||||
|
KCL Semantic error
|
||||||
|
|
||||||
|
× semantic: This function requires a parameter y, but you haven't passed
|
||||||
|
│ it one.
|
||||||
|
╭─[1:7]
|
||||||
|
1 │ ╭─▶ fn add(x, y) {
|
||||||
|
2 │ │ return x + y
|
||||||
|
3 │ ╰─▶ }
|
||||||
|
4 │
|
||||||
|
5 │ three = add(x: 1)
|
||||||
|
· ─────────
|
||||||
|
╰────
|
||||||
5
src/wasm-lib/kcl/tests/kw_fn_too_few_args/input.kcl
Normal file
5
src/wasm-lib/kcl/tests/kw_fn_too_few_args/input.kcl
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
fn add(x, y) {
|
||||||
|
return x + y
|
||||||
|
}
|
||||||
|
|
||||||
|
three = add(x: 1)
|
||||||
146
src/wasm-lib/kcl/tests/kw_fn_unlabeled_but_has_label/ast.snap
Normal file
146
src/wasm-lib/kcl/tests/kw_fn_unlabeled_but_has_label/ast.snap
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Result of parsing kw_fn_unlabeled_but_has_label.kcl
|
||||||
|
snapshot_kind: text
|
||||||
|
---
|
||||||
|
{
|
||||||
|
"Ok": {
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"declaration": {
|
||||||
|
"end": 29,
|
||||||
|
"id": {
|
||||||
|
"end": 6,
|
||||||
|
"name": "add",
|
||||||
|
"start": 3,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"init": {
|
||||||
|
"body": {
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"argument": {
|
||||||
|
"end": 27,
|
||||||
|
"left": {
|
||||||
|
"end": 23,
|
||||||
|
"name": "x",
|
||||||
|
"start": 22,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"operator": "+",
|
||||||
|
"right": {
|
||||||
|
"end": 27,
|
||||||
|
"raw": "1",
|
||||||
|
"start": 26,
|
||||||
|
"type": "Literal",
|
||||||
|
"type": "Literal",
|
||||||
|
"value": 1.0
|
||||||
|
},
|
||||||
|
"start": 22,
|
||||||
|
"type": "BinaryExpression",
|
||||||
|
"type": "BinaryExpression"
|
||||||
|
},
|
||||||
|
"end": 27,
|
||||||
|
"start": 15,
|
||||||
|
"type": "ReturnStatement",
|
||||||
|
"type": "ReturnStatement"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"end": 29,
|
||||||
|
"start": 11
|
||||||
|
},
|
||||||
|
"end": 29,
|
||||||
|
"params": [
|
||||||
|
{
|
||||||
|
"type": "Parameter",
|
||||||
|
"identifier": {
|
||||||
|
"end": 9,
|
||||||
|
"name": "x",
|
||||||
|
"start": 8,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"labeled": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"start": 6,
|
||||||
|
"type": "FunctionExpression",
|
||||||
|
"type": "FunctionExpression"
|
||||||
|
},
|
||||||
|
"start": 3,
|
||||||
|
"type": "VariableDeclarator"
|
||||||
|
},
|
||||||
|
"end": 29,
|
||||||
|
"kind": "fn",
|
||||||
|
"start": 0,
|
||||||
|
"type": "VariableDeclaration",
|
||||||
|
"type": "VariableDeclaration"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"declaration": {
|
||||||
|
"end": 46,
|
||||||
|
"id": {
|
||||||
|
"end": 34,
|
||||||
|
"name": "two",
|
||||||
|
"start": 31,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"init": {
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"type": "LabeledArg",
|
||||||
|
"label": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"name": "x"
|
||||||
|
},
|
||||||
|
"arg": {
|
||||||
|
"end": 45,
|
||||||
|
"raw": "1",
|
||||||
|
"start": 44,
|
||||||
|
"type": "Literal",
|
||||||
|
"type": "Literal",
|
||||||
|
"value": 1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"callee": {
|
||||||
|
"end": 40,
|
||||||
|
"name": "add",
|
||||||
|
"start": 37,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"end": 46,
|
||||||
|
"start": 37,
|
||||||
|
"type": "CallExpressionKw",
|
||||||
|
"type": "CallExpressionKw",
|
||||||
|
"unlabeled": null
|
||||||
|
},
|
||||||
|
"start": 31,
|
||||||
|
"type": "VariableDeclarator"
|
||||||
|
},
|
||||||
|
"end": 46,
|
||||||
|
"kind": "const",
|
||||||
|
"start": 31,
|
||||||
|
"type": "VariableDeclaration",
|
||||||
|
"type": "VariableDeclaration"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"end": 47,
|
||||||
|
"nonCodeMeta": {
|
||||||
|
"nonCodeNodes": {
|
||||||
|
"0": [
|
||||||
|
{
|
||||||
|
"end": 31,
|
||||||
|
"start": 29,
|
||||||
|
"type": "NonCodeNode",
|
||||||
|
"value": {
|
||||||
|
"type": "newLine"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"startNodes": []
|
||||||
|
},
|
||||||
|
"start": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Error from executing kw_fn_unlabeled_but_has_label.kcl
|
||||||
|
snapshot_kind: text
|
||||||
|
---
|
||||||
|
KCL Semantic error
|
||||||
|
|
||||||
|
× semantic: The function does declare a parameter named 'x', but this
|
||||||
|
│ parameter doesn't use a label. Try removing the `x:`
|
||||||
|
╭─[1:7]
|
||||||
|
1 │ ╭─▶ fn add(@x) {
|
||||||
|
2 │ │ return x + 1
|
||||||
|
3 │ ╰─▶ }
|
||||||
|
4 │
|
||||||
|
5 │ two = add(x: 1)
|
||||||
|
· ─────────
|
||||||
|
╰────
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
fn add(@x) {
|
||||||
|
return x + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
two = add(x: 1)
|
||||||
Reference in New Issue
Block a user