devalang_wasm/engine/audio/interpreter/statements/
arrow_call.rs

1use crate::engine::functions::FunctionContext;
2/// Arrow call statement handler
3use anyhow::{Result, anyhow};
4
5pub fn execute_arrow_call(
6    interpreter: &mut crate::engine::audio::interpreter::driver::AudioInterpreter,
7    target: &str,
8    method: &str,
9    args: &[crate::language::syntax::ast::Value],
10    chain: Option<&[crate::language::syntax::ast::Value]>,
11    cursor_time: f32,
12    tempo: f32,
13) -> Result<FunctionContext> {
14    // Create initial context
15    let mut context = FunctionContext {
16        target: target.to_string(),
17        state: std::collections::HashMap::new(),
18        start_time: cursor_time,
19        duration: 0.0,
20        tempo,
21    };
22
23    // Record the invocation method (so downstream can distinguish note vs synth chained calls)
24    context.set(
25        "method",
26        crate::language::syntax::ast::Value::String(method.to_string()),
27    );
28
29    // Resolve argument identifiers to runtime values before executing functions
30    let resolved_args: Vec<crate::language::syntax::ast::Value> = args
31        .iter()
32        .map(|v| interpreter.resolve_value(v))
33        .collect::<Result<Vec<_>>>()?;
34
35    // Execute first method
36    if interpreter.function_registry.has(method) {
37        interpreter
38            .function_registry
39            .execute(method, &mut context, &resolved_args)?;
40    } else {
41        // Not a registered function: treat as a routing/target name
42        context.set(
43            "route_to",
44            crate::language::syntax::ast::Value::String(method.to_string()),
45        );
46    }
47
48    // Execute chained methods if any
49    if let Some(chain_calls) = chain {
50        // Collect unknown chained calls as effect definitions so they can be attached to the
51        // resulting FunctionContext for later extraction into per-event effects.
52        let mut unknown_effects: Vec<crate::language::syntax::ast::Value> = Vec::new();
53        for call_value in chain_calls {
54            if let crate::language::syntax::ast::Value::Map(call_map) = call_value {
55                // Extract method and args from map
56                let method_name = call_map
57                    .get("method")
58                    .and_then(|v| {
59                        if let crate::language::syntax::ast::Value::String(s) = v {
60                            Some(s.as_str())
61                        } else {
62                            None
63                        }
64                    })
65                    .ok_or_else(|| anyhow!("Chain call missing method name"))?;
66
67                let method_args = call_map
68                    .get("args")
69                    .and_then(|v| {
70                        if let crate::language::syntax::ast::Value::Array(a) = v {
71                            Some(a.as_slice())
72                        } else {
73                            None
74                        }
75                    })
76                    .unwrap_or(&[]);
77
78                // Resolve chained method args using interpreter
79                let resolved_method_args: Vec<crate::language::syntax::ast::Value> = method_args
80                    .iter()
81                    .map(|v| interpreter.resolve_value(v))
82                    .collect::<Result<Vec<_>>>()?;
83
84                // Execute chained method if known; otherwise collect as effect map
85                if interpreter.function_registry.has(method_name) {
86                    interpreter.function_registry.execute(
87                        method_name,
88                        &mut context,
89                        &resolved_method_args,
90                    )?;
91                } else {
92                    // Convert chained call_map (method+args) into an effect-style map
93                    // Expected effect map shape: { "type": "lfo", ...params... }
94                    let mut effect_map: std::collections::HashMap<
95                        String,
96                        crate::language::syntax::ast::Value,
97                    > = std::collections::HashMap::new();
98                    effect_map.insert(
99                        "type".to_string(),
100                        crate::language::syntax::ast::Value::String(method_name.to_string()),
101                    );
102
103                    // If first arg is a Map, merge it as params
104                    if let Some(first_arg) = resolved_method_args.get(0) {
105                        if let crate::language::syntax::ast::Value::Map(m) = first_arg {
106                            for (k, v) in m.iter() {
107                                effect_map.insert(k.clone(), v.clone());
108                            }
109                        } else {
110                            // otherwise, store as single 'value' param
111                            effect_map.insert("value".to_string(), first_arg.clone());
112                        }
113                    }
114
115                    unknown_effects.push(crate::language::syntax::ast::Value::Map(effect_map));
116                }
117            }
118        }
119
120        if !unknown_effects.is_empty() {
121            context.set(
122                "effects",
123                crate::language::syntax::ast::Value::Array(unknown_effects),
124            );
125        }
126    }
127
128    Ok(context)
129}