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]
|
||||
pub async fn execute(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
|
||||
let fn_name = &self.callee.name;
|
||||
let callsite: SourceRange = self.into();
|
||||
|
||||
// Build a hashmap from argument labels to the final evaluated values.
|
||||
let mut fn_args = HashMap::with_capacity(self.arguments.len());
|
||||
@ -412,7 +413,39 @@ impl Node<CallExpressionKw> {
|
||||
Ok(result)
|
||||
}
|
||||
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),
|
||||
#[ts(skip)]
|
||||
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)]
|
||||
func: Option<MemoryFunction>,
|
||||
#[schemars(skip)]
|
||||
@ -503,4 +507,39 @@ impl KclValue {
|
||||
.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)
|
||||
}
|
||||
|
||||
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(
|
||||
args: Vec<Arg>,
|
||||
memory: &ProgramMemory,
|
||||
@ -2315,6 +2368,36 @@ pub(crate) async fn call_user_defined_function(
|
||||
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> {
|
||||
Declaration { name: &'a str },
|
||||
Expression,
|
||||
|
||||
@ -1544,3 +1544,45 @@ mod tag_proxied_through_function_does_not_define_var {
|
||||
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>,
|
||||
}
|
||||
|
||||
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)]
|
||||
pub struct Args {
|
||||
/// Positional args.
|
||||
|
||||
@ -78,46 +78,179 @@ snapshot_kind: text
|
||||
},
|
||||
{
|
||||
"declaration": {
|
||||
"end": 55,
|
||||
"end": 77,
|
||||
"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",
|
||||
"start": 37,
|
||||
"start": 79,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"init": {
|
||||
"arguments": [
|
||||
{
|
||||
"end": 54,
|
||||
"end": 96,
|
||||
"raw": "1",
|
||||
"start": 53,
|
||||
"start": 95,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": 1.0
|
||||
}
|
||||
],
|
||||
"callee": {
|
||||
"end": 52,
|
||||
"end": 94,
|
||||
"name": "increment",
|
||||
"start": 43,
|
||||
"start": 85,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"end": 55,
|
||||
"start": 43,
|
||||
"end": 97,
|
||||
"start": 85,
|
||||
"type": "CallExpression",
|
||||
"type": "CallExpression"
|
||||
},
|
||||
"start": 37,
|
||||
"start": 79,
|
||||
"type": "VariableDeclarator"
|
||||
},
|
||||
"end": 55,
|
||||
"end": 97,
|
||||
"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"
|
||||
}
|
||||
],
|
||||
"end": 56,
|
||||
"end": 123,
|
||||
"nonCodeMeta": {
|
||||
"nonCodeNodes": {
|
||||
"0": [
|
||||
@ -129,6 +262,16 @@ snapshot_kind: text
|
||||
"type": "newLine"
|
||||
}
|
||||
}
|
||||
],
|
||||
"1": [
|
||||
{
|
||||
"end": 79,
|
||||
"start": 77,
|
||||
"type": "NonCodeNode",
|
||||
"value": {
|
||||
"type": "newLine"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"startNodes": []
|
||||
|
||||
@ -2,4 +2,9 @@ fn increment(@x) {
|
||||
return x + 1
|
||||
}
|
||||
|
||||
fn add(@x, delta) {
|
||||
return x + delta
|
||||
}
|
||||
|
||||
two = increment(1)
|
||||
three = add(1, delta: 2)
|
||||
|
||||
@ -27,6 +27,202 @@ snapshot_kind: text
|
||||
"value": 0.0,
|
||||
"__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": {
|
||||
"type": "Function",
|
||||
"expression": {
|
||||
@ -121,14 +317,34 @@ snapshot_kind: text
|
||||
}
|
||||
]
|
||||
},
|
||||
"three": {
|
||||
"type": "Number",
|
||||
"value": 3.0,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
110,
|
||||
111,
|
||||
0
|
||||
]
|
||||
},
|
||||
{
|
||||
"sourceRange": [
|
||||
120,
|
||||
121,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"two": {
|
||||
"type": "Number",
|
||||
"value": 2.0,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
53,
|
||||
54,
|
||||
95,
|
||||
96,
|
||||
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