gent/interpreter/
block_eval.rs

1//! Block evaluation module
2//!
3//! This module provides async block evaluation for executing tool bodies
4//! with let bindings, return statements, if/else, and expression statements.
5
6use crate::errors::{GentError, GentResult};
7use crate::interpreter::builtins::{call_builtin, is_builtin};
8use crate::interpreter::expr_eval::evaluate_expr;
9use crate::interpreter::array_methods::{call_array_method, call_array_method_with_callback, is_callback_method};
10use crate::interpreter::string_methods::call_string_method;
11use crate::interpreter::types::EnumValue;
12use crate::interpreter::{Environment, Value};
13use crate::logging::{Logger, NullLogger};
14use crate::parser::ast::{Block, BlockStmt, Expression, MatchBody, MatchPattern};
15use crate::runtime::tools::ToolRegistry;
16use crate::runtime::{run_agent_with_tools, LLMClient};
17use std::collections::HashMap;
18
19/// Context for block evaluation that includes optional LLM client for agent execution
20pub struct BlockEvalContext<'a> {
21    pub llm: Option<&'a dyn LLMClient>,
22    pub logger: &'a dyn Logger,
23}
24
25impl<'a> BlockEvalContext<'a> {
26    /// Create a new context with LLM client
27    pub fn with_llm(llm: &'a dyn LLMClient, logger: &'a dyn Logger) -> Self {
28        Self { llm: Some(llm), logger }
29    }
30
31    /// Create an empty context (no agent execution support)
32    pub fn empty() -> Self {
33        // Use a static NullLogger for the empty context
34        static NULL_LOGGER: NullLogger = NullLogger;
35        Self { llm: None, logger: &NULL_LOGGER }
36    }
37}
38
39/// Control flow signal for break/continue/return propagation
40#[derive(Debug, Clone, PartialEq)]
41enum ControlFlow {
42    /// Normal execution, continue to next statement
43    Continue,
44    /// Break out of the current loop
45    Break,
46    /// Skip to next iteration of the current loop
47    LoopContinue,
48    /// Return from the function with the given value (boxed to reduce enum size)
49    Return(Box<Value>),
50}
51
52/// Type alias for async block evaluation result with control flow
53type BlockInternalFuture<'a> =
54    std::pin::Pin<Box<dyn std::future::Future<Output = GentResult<(ControlFlow, Value)>> + 'a>>;
55
56/// Evaluate a block of statements in the given environment
57///
58/// Returns the value of the first return statement encountered,
59/// or Value::Null if the block completes without returning.
60///
61/// Note: This version does not support agent execution. Use `evaluate_block_with_llm`
62/// if you need to call agent methods like `.run()`.
63pub fn evaluate_block<'a>(
64    block: &'a Block,
65    env: &'a mut Environment,
66    tools: &'a ToolRegistry,
67) -> std::pin::Pin<Box<dyn std::future::Future<Output = GentResult<Value>> + 'a>> {
68    Box::pin(async move {
69        // Create a new scope for this block
70        env.push_scope();
71
72        let ctx = BlockEvalContext::empty();
73        let (flow, result) = evaluate_block_internal(block, env, tools, &ctx).await?;
74
75        // Pop the scope
76        env.pop_scope();
77
78        // Handle control flow that escaped the block
79        match flow {
80            ControlFlow::Return(val) => Ok(*val),
81            ControlFlow::Continue => Ok(result),
82            // Break/LoopContinue outside of a loop is an error, but we treat it as normal
83            // completion for now (the loop handler consumes these signals)
84            ControlFlow::Break | ControlFlow::LoopContinue => Ok(result),
85        }
86    })
87}
88
89/// Evaluate a block with LLM support for agent execution
90///
91/// This version supports calling agent methods like `.run()` within the block.
92pub fn evaluate_block_with_llm<'a>(
93    block: &'a Block,
94    env: &'a mut Environment,
95    tools: &'a ToolRegistry,
96    llm: &'a dyn LLMClient,
97    logger: &'a dyn Logger,
98) -> std::pin::Pin<Box<dyn std::future::Future<Output = GentResult<Value>> + 'a>> {
99    Box::pin(async move {
100        // Create a new scope for this block
101        env.push_scope();
102
103        let ctx = BlockEvalContext::with_llm(llm, logger);
104        let (flow, result) = evaluate_block_internal(block, env, tools, &ctx).await?;
105
106        // Pop the scope
107        env.pop_scope();
108
109        // Handle control flow that escaped the block
110        match flow {
111            ControlFlow::Return(val) => Ok(*val),
112            ControlFlow::Continue => Ok(result),
113            ControlFlow::Break | ControlFlow::LoopContinue => Ok(result),
114        }
115    })
116}
117
118/// Evaluate a block with an existing context
119///
120/// This is used internally when calling functions to preserve the LLM context.
121fn evaluate_block_with_ctx<'a>(
122    block: &'a Block,
123    env: &'a mut Environment,
124    tools: &'a ToolRegistry,
125    ctx: &'a BlockEvalContext<'a>,
126) -> std::pin::Pin<Box<dyn std::future::Future<Output = GentResult<Value>> + 'a>> {
127    Box::pin(async move {
128        // Create a new scope for this block
129        env.push_scope();
130
131        let (flow, result) = evaluate_block_internal(block, env, tools, ctx).await?;
132
133        // Pop the scope
134        env.pop_scope();
135
136        // Handle control flow that escaped the block
137        match flow {
138            ControlFlow::Return(val) => Ok(*val),
139            ControlFlow::Continue => Ok(result),
140            ControlFlow::Break | ControlFlow::LoopContinue => Ok(result),
141        }
142    })
143}
144
145/// Internal block evaluation that returns control flow signals
146fn evaluate_block_internal<'a>(
147    block: &'a Block,
148    env: &'a mut Environment,
149    tools: &'a ToolRegistry,
150    ctx: &'a BlockEvalContext<'a>,
151) -> BlockInternalFuture<'a> {
152    Box::pin(async move {
153        let mut result = Value::Null;
154
155        for stmt in &block.statements {
156            match stmt {
157                BlockStmt::Let(let_stmt) => {
158                    // Check if the value is a mutating array method call (push/pop)
159                    let value = if let Some((arr_var, method_name, args)) = extract_array_method_call(&let_stmt.value) {
160                        if method_name == "push" || method_name == "pop" {
161                            if let Some(Value::Array(arr)) = env.get(&arr_var).cloned() {
162                                let mut arr_mut = arr;
163
164                                // Evaluate arguments
165                                let mut arg_values = Vec::new();
166                                for arg in args {
167                                    let val = evaluate_expr_async(arg, env, tools, ctx).await?;
168                                    arg_values.push(val);
169                                }
170
171                                // Call the method and get result
172                                let result = call_array_method(&mut arr_mut, &method_name, &arg_values)?;
173
174                                // Update the array variable with the mutated array
175                                env.set(&arr_var, Value::Array(arr_mut));
176
177                                result
178                            } else {
179                                evaluate_expr_async(&let_stmt.value, env, tools, ctx).await?
180                            }
181                        } else {
182                            evaluate_expr_async(&let_stmt.value, env, tools, ctx).await?
183                        }
184                    } else {
185                        evaluate_expr_async(&let_stmt.value, env, tools, ctx).await?
186                    };
187
188                    // Define the variable in the current scope
189                    env.define(&let_stmt.name, value);
190                }
191
192                BlockStmt::Assignment(assign_stmt) => {
193                    // Evaluate the right-hand side expression
194                    let value = evaluate_expr_async(&assign_stmt.value, env, tools, ctx).await?;
195
196                    // Update the variable in the environment
197                    if !env.set(&assign_stmt.name, value) {
198                        return Err(GentError::SyntaxError {
199                            message: format!("Undefined variable: '{}'", assign_stmt.name),
200                            span: assign_stmt.span.clone(),
201                        });
202                    }
203                }
204
205                BlockStmt::Return(return_stmt) => {
206                    // Evaluate the return value (if any)
207                    result = if let Some(ref expr) = return_stmt.value {
208                        evaluate_expr_async(expr, env, tools, ctx).await?
209                    } else {
210                        Value::Null
211                    };
212                    return Ok((ControlFlow::Return(Box::new(result)), Value::Null));
213                }
214
215                BlockStmt::If(if_stmt) => {
216                    // Evaluate the condition
217                    let condition = evaluate_expr_async(&if_stmt.condition, env, tools, ctx).await?;
218
219                    // Execute the appropriate block
220                    if condition.is_truthy() {
221                        // Execute then block (create a new scope)
222                        env.push_scope();
223                        let (flow, _) = evaluate_block_internal(&if_stmt.then_block, env, tools, ctx).await?;
224                        env.pop_scope();
225
226                        // Propagate control flow signals
227                        match flow {
228                            ControlFlow::Continue => {}
229                            other => return Ok((other, Value::Null)),
230                        }
231                    } else if let Some(ref else_block) = if_stmt.else_block {
232                        // Execute else block (create a new scope)
233                        env.push_scope();
234                        let (flow, _) = evaluate_block_internal(else_block, env, tools, ctx).await?;
235                        env.pop_scope();
236
237                        // Propagate control flow signals
238                        match flow {
239                            ControlFlow::Continue => {}
240                            other => return Ok((other, Value::Null)),
241                        }
242                    }
243                }
244
245                BlockStmt::For(for_stmt) => {
246                    // Evaluate the iterable expression
247                    let iterable = evaluate_expr(&for_stmt.iterable, env)?;
248
249                    // Convert iterable to a list of items
250                    let items: Vec<Value> = match iterable {
251                        Value::Array(arr) => arr,
252                        Value::String(s) => s.chars().map(|c| Value::String(c.to_string())).collect(),
253                        other => {
254                            return Err(GentError::TypeError {
255                                expected: "Array or String".to_string(),
256                                got: other.type_name().to_string(),
257                                span: for_stmt.span.clone(),
258                            });
259                        }
260                    };
261
262                    // Iterate over items
263                    'outer: for item in items {
264                        env.push_scope();
265                        env.define(&for_stmt.variable, item);
266
267                        // Execute the loop body using internal evaluation
268                        let (flow, _) = evaluate_block_internal(&for_stmt.body, env, tools, ctx).await?;
269
270                        env.pop_scope();
271
272                        // Handle control flow from the loop body
273                        match flow {
274                            ControlFlow::Continue => {
275                                // Normal completion, continue to next iteration
276                            }
277                            ControlFlow::LoopContinue => {
278                                // Skip to next iteration (already handled by continuing the loop)
279                                continue 'outer;
280                            }
281                            ControlFlow::Break => {
282                                // Exit the loop
283                                break 'outer;
284                            }
285                            ControlFlow::Return(val) => {
286                                // Propagate return up
287                                return Ok((ControlFlow::Return(val), Value::Null));
288                            }
289                        }
290                    }
291                }
292
293                BlockStmt::Expr(expr) => {
294                    // Check for mutating array method calls (push/pop) and handle specially
295                    if let Some((var_name, method_name, args)) = extract_array_method_call(expr) {
296                        if method_name == "push" || method_name == "pop" {
297                            // Get the current array value
298                            if let Some(Value::Array(arr)) = env.get(&var_name).cloned() {
299                                let mut arr_mut = arr;
300
301                                // Evaluate arguments
302                                let mut arg_values = Vec::new();
303                                for arg in args {
304                                    let val = evaluate_expr_async(arg, env, tools, ctx).await?;
305                                    arg_values.push(val);
306                                }
307
308                                // Call the method
309                                call_array_method(&mut arr_mut, &method_name, &arg_values)?;
310
311                                // Update the variable with the mutated array
312                                env.set(&var_name, Value::Array(arr_mut));
313                            }
314                            continue;
315                        }
316                    }
317
318                    // Evaluate the expression for side effects, discarding the result
319                    evaluate_expr_async(expr, env, tools, ctx).await?;
320                }
321
322                BlockStmt::While(while_stmt) => {
323                    const MAX_ITERATIONS: usize = 10000; // Prevent infinite loops
324                    let mut iterations = 0;
325
326                    'while_loop: loop {
327                        iterations += 1;
328                        if iterations > MAX_ITERATIONS {
329                            return Err(GentError::SyntaxError {
330                                message: format!(
331                                    "While loop exceeded maximum iterations ({})",
332                                    MAX_ITERATIONS
333                                ),
334                                span: while_stmt.span.clone(),
335                            });
336                        }
337
338                        // Evaluate condition
339                        let condition =
340                            evaluate_expr_async(&while_stmt.condition, env, tools, ctx).await?;
341                        if !condition.is_truthy() {
342                            break;
343                        }
344
345                        // Execute body statements with a new scope
346                        env.push_scope();
347                        let (flow, _) =
348                            evaluate_block_internal(&while_stmt.body, env, tools, ctx).await?;
349                        env.pop_scope();
350
351                        // Handle control flow from the loop body
352                        match flow {
353                            ControlFlow::Continue => {
354                                // Normal completion, continue to next iteration
355                            }
356                            ControlFlow::LoopContinue => {
357                                // Skip to next iteration
358                                continue 'while_loop;
359                            }
360                            ControlFlow::Break => {
361                                // Exit the loop
362                                break 'while_loop;
363                            }
364                            ControlFlow::Return(val) => {
365                                // Propagate return up
366                                return Ok((ControlFlow::Return(val), Value::Null));
367                            }
368                        }
369                    }
370                }
371
372                BlockStmt::Break(_) => {
373                    // Signal break to the enclosing loop
374                    return Ok((ControlFlow::Break, Value::Null));
375                }
376
377                BlockStmt::Continue(_) => {
378                    // Signal continue to the enclosing loop
379                    return Ok((ControlFlow::LoopContinue, Value::Null));
380                }
381
382                BlockStmt::Try(try_stmt) => {
383                    // Execute try block and capture result
384                    env.push_scope();
385                    let try_result = evaluate_block_internal(&try_stmt.try_block, env, tools, ctx).await;
386                    env.pop_scope();
387
388                    match try_result {
389                        Ok((flow, _value)) => {
390                            // Try block succeeded
391                            match flow {
392                                ControlFlow::Return(val) => {
393                                    return Ok((ControlFlow::Return(val), Value::Null));
394                                }
395                                ControlFlow::Break => {
396                                    return Ok((ControlFlow::Break, Value::Null));
397                                }
398                                ControlFlow::LoopContinue => {
399                                    return Ok((ControlFlow::LoopContinue, Value::Null));
400                                }
401                                ControlFlow::Continue => {
402                                    // Normal completion, continue with next statement after try/catch
403                                }
404                            }
405                        }
406                        Err(e) => {
407                            // Error occurred, execute catch block with error bound
408                            env.push_scope();
409                            env.define(&try_stmt.error_var, Value::String(e.to_string()));
410
411                            let catch_result =
412                                evaluate_block_internal(&try_stmt.catch_block, env, tools, ctx).await?;
413                            env.pop_scope();
414
415                            match catch_result.0 {
416                                ControlFlow::Return(val) => {
417                                    return Ok((ControlFlow::Return(val), Value::Null));
418                                }
419                                ControlFlow::Break => {
420                                    return Ok((ControlFlow::Break, Value::Null));
421                                }
422                                ControlFlow::LoopContinue => {
423                                    return Ok((ControlFlow::LoopContinue, Value::Null));
424                                }
425                                ControlFlow::Continue => {
426                                    // Normal completion, continue with next statement after try/catch
427                                }
428                            }
429                        }
430                    }
431                }
432            }
433        }
434
435        Ok((ControlFlow::Continue, result))
436    })
437}
438
439/// Evaluate an expression in an async context, handling function calls
440///
441/// This function is similar to evaluate_expr but supports async tool calls.
442/// The `ctx` parameter provides optional LLM client for agent execution.
443pub fn evaluate_expr_async<'a>(
444    expr: &'a Expression,
445    env: &'a Environment,
446    tools: &'a ToolRegistry,
447    ctx: &'a BlockEvalContext<'a>,
448) -> std::pin::Pin<Box<dyn std::future::Future<Output = GentResult<Value>> + 'a>> {
449    Box::pin(async move {
450        match expr {
451            // Function/tool calls require async context
452            Expression::Call(callee_expr, args, span) => {
453                // Check if this is a method call on a string, array, or enum constructor
454                if let Expression::Member(obj_expr, method_name, _) = callee_expr.as_ref() {
455                    // First check if this could be an enum constructor call: EnumName.Variant(args)
456                    if let Expression::Identifier(name, _) = obj_expr.as_ref() {
457                        if let Some(enum_def) = env.get_enum(name) {
458                            // Find the variant
459                            if let Some(v) = enum_def.variants.iter().find(|v| v.name == *method_name) {
460                                // Evaluate arguments
461                                let mut arg_values = Vec::new();
462                                for arg in args {
463                                    let val = evaluate_expr_async(arg, env, tools, ctx).await?;
464                                    arg_values.push(val);
465                                }
466
467                                if arg_values.len() != v.fields.len() {
468                                    return Err(GentError::TypeError {
469                                        expected: format!(
470                                            "Variant '{}' expects {} arguments",
471                                            method_name, v.fields.len()
472                                        ),
473                                        got: format!("{} arguments", arg_values.len()),
474                                        span: span.clone(),
475                                    });
476                                }
477
478                                return Ok(Value::Enum(EnumValue {
479                                    enum_name: name.clone(),
480                                    variant: method_name.clone(),
481                                    data: arg_values,
482                                }));
483                            } else {
484                                return Err(GentError::TypeError {
485                                    expected: format!("valid variant of enum '{}'", name),
486                                    got: method_name.clone(),
487                                    span: span.clone(),
488                                });
489                            }
490                        }
491                    }
492
493                    // Evaluate the object expression
494                    let obj = evaluate_expr_async(obj_expr, env, tools, ctx).await?;
495
496                    // If it's a string, dispatch to string methods
497                    if let Value::String(s) = &obj {
498                        // Evaluate method arguments
499                        let mut arg_values = Vec::new();
500                        for arg in args {
501                            let val = evaluate_expr_async(arg, env, tools, ctx).await?;
502                            arg_values.push(val);
503                        }
504                        return call_string_method(s, method_name, &arg_values);
505                    }
506
507                    // Check if this is an enum .is() or .data() call
508                    if let Value::Enum(ref enum_val) = obj {
509                        if method_name == "is" {
510                            // Evaluate the argument (should be an EnumValue or EnumConstructor)
511                            if args.len() != 1 {
512                                return Err(GentError::TypeError {
513                                    expected: "1 argument for .is()".to_string(),
514                                    got: format!("{} arguments", args.len()),
515                                    span: span.clone(),
516                                });
517                            }
518
519                            let arg = evaluate_expr_async(&args[0], env, tools, ctx).await?;
520                            let matches = match arg {
521                                Value::Enum(other) => {
522                                    enum_val.enum_name == other.enum_name && enum_val.variant == other.variant
523                                }
524                                Value::EnumConstructor(ctor) => {
525                                    enum_val.enum_name == ctor.enum_name && enum_val.variant == ctor.variant
526                                }
527                                _ => false,
528                            };
529
530                            return Ok(Value::Boolean(matches));
531                        }
532
533                        if method_name == "data" {
534                            if args.len() != 1 {
535                                return Err(GentError::TypeError {
536                                    expected: "1 argument for .data()".to_string(),
537                                    got: format!("{} arguments", args.len()),
538                                    span: span.clone(),
539                                });
540                            }
541
542                            let arg = evaluate_expr_async(&args[0], env, tools, ctx).await?;
543
544                            match arg {
545                                Value::Number(n) => {
546                                    let idx = n as usize;
547                                    return Ok(enum_val.data.get(idx).cloned().unwrap_or(Value::Null));
548                                }
549                                Value::String(_) => {
550                                    // Named access not yet implemented
551                                    return Err(GentError::TypeError {
552                                        expected: "number index for .data()".to_string(),
553                                        got: "string (named access not yet implemented)".to_string(),
554                                        span: span.clone(),
555                                    });
556                                }
557                                _ => {
558                                    return Err(GentError::TypeError {
559                                        expected: "number index".to_string(),
560                                        got: arg.type_name(),
561                                        span: span.clone(),
562                                    });
563                                }
564                            }
565                        }
566                    }
567
568                    // If it's an array, dispatch to array methods
569                    if let Value::Array(ref arr) = obj {
570                        // Evaluate method arguments
571                        let mut arg_values = Vec::new();
572                        for arg in args {
573                            let val = evaluate_expr_async(arg, env, tools, ctx).await?;
574                            arg_values.push(val);
575                        }
576
577                        // Check if this is a callback method (map, filter, reduce, find)
578                        if is_callback_method(method_name) {
579                            let callback = arg_values.first().ok_or_else(|| GentError::TypeError {
580                                expected: "callback function for array method".to_string(),
581                                got: "missing argument".to_string(),
582                                span: span.clone(),
583                            })?;
584                            let extra_args = if arg_values.len() > 1 { &arg_values[1..] } else { &[] };
585                            return call_array_method_with_callback(
586                                arr,
587                                method_name,
588                                callback,
589                                extra_args,
590                                env,
591                                tools,
592                            ).await;
593                        }
594
595                        // Non-callback methods
596                        let mut arr_clone = arr.clone();
597                        let result = call_array_method(
598                            &mut arr_clone,
599                            method_name,
600                            &arg_values,
601                        )?;
602
603                        return Ok(result);
604                    }
605
606                    // Handle Agent method calls (userPrompt, systemPrompt, run)
607                    if let Value::Agent(mut agent) = obj {
608                        match method_name.as_str() {
609                            "userPrompt" => {
610                                // Set user_prompt and return modified agent
611                                if args.is_empty() {
612                                    return Err(GentError::SyntaxError {
613                                        message: "userPrompt() requires an argument".to_string(),
614                                        span: span.clone(),
615                                    });
616                                }
617                                let arg = evaluate_expr_async(&args[0], env, tools, ctx).await?;
618                                let prompt = match arg {
619                                    Value::String(s) => s,
620                                    other => format!("{}", other),
621                                };
622                                agent.user_prompt = Some(prompt);
623                                return Ok(Value::Agent(agent));
624                            }
625                            "systemPrompt" => {
626                                // Set system_prompt and return modified agent
627                                if args.is_empty() {
628                                    return Err(GentError::SyntaxError {
629                                        message: "systemPrompt() requires an argument".to_string(),
630                                        span: span.clone(),
631                                    });
632                                }
633                                let arg = evaluate_expr_async(&args[0], env, tools, ctx).await?;
634                                let prompt = match arg {
635                                    Value::String(s) => s,
636                                    other => format!("{}", other),
637                                };
638                                agent.system_prompt = prompt;
639                                return Ok(Value::Agent(agent));
640                            }
641                            "run" => {
642                                // Execute the agent - requires LLM client
643                                if let Some(llm) = ctx.llm {
644                                    let result = run_agent_with_tools(&agent, None, llm, tools, ctx.logger).await?;
645                                    // If agent has structured output, parse as JSON
646                                    if agent.output_schema.is_some() {
647                                        if let Ok(json_val) = serde_json::from_str::<serde_json::Value>(&result) {
648                                            return Ok(json_to_value(&json_val));
649                                        }
650                                    }
651                                    return Ok(Value::String(result));
652                                } else {
653                                    return Err(GentError::SyntaxError {
654                                        message: "Cannot call .run() on agent in this context (no LLM client available)".to_string(),
655                                        span: span.clone(),
656                                    });
657                                }
658                            }
659                            _ => {
660                                return Err(GentError::SyntaxError {
661                                    message: format!("Unknown agent method: {}", method_name),
662                                    span: span.clone(),
663                                });
664                            }
665                        }
666                    }
667
668                    // For other types, return an error for now
669                    return Err(GentError::TypeError {
670                        expected: "String, Array, or Agent".to_string(),
671                        got: obj.type_name().to_string(),
672                        span: span.clone(),
673                    });
674                }
675
676                // Get the callable name
677                let callable_name = if let Expression::Identifier(name, _) = callee_expr.as_ref() {
678                    name.clone()
679                } else {
680                    let callee = evaluate_expr(callee_expr, env)?;
681                    return Err(GentError::TypeError {
682                        expected: "function or tool name".to_string(),
683                        got: callee.type_name().to_string(),
684                        span: span.clone(),
685                    });
686                };
687
688                // Evaluate arguments first (needed for both functions and tools)
689                let mut arg_values = Vec::new();
690                for arg in args {
691                    let val = evaluate_expr_async(arg, env, tools, ctx).await?;
692                    arg_values.push(val);
693                }
694
695                // Check if it's a built-in function
696                if is_builtin(&callable_name) {
697                    return call_builtin(&callable_name, &arg_values, span);
698                }
699
700                // Check if it's a function in the environment
701                if let Some(Value::Function(fn_val)) = env.get(&callable_name) {
702                    // Clone the function value since we need to borrow env mutably later
703                    let fn_val = fn_val.clone();
704
705                    // Check argument count
706                    if arg_values.len() != fn_val.params.len() {
707                        return Err(GentError::SyntaxError {
708                            message: format!(
709                                "Function '{}' expects {} arguments, got {}",
710                                fn_val.name,
711                                fn_val.params.len(),
712                                arg_values.len()
713                            ),
714                            span: span.clone(),
715                        });
716                    }
717
718                    // Create a new environment with function scope
719                    let mut fn_env = env.clone();
720                    fn_env.push_scope();
721
722                    // Bind parameters to arguments
723                    for (param, arg_val) in fn_val.params.iter().zip(arg_values.iter()) {
724                        fn_env.define(&param.name, arg_val.clone());
725                    }
726
727                    // Evaluate the function body
728                    let result = evaluate_block_with_ctx(&fn_val.body, &mut fn_env, tools, ctx).await?;
729                    return Ok(result);
730                }
731
732                // Look up the tool in the registry
733                let tool = tools
734                    .get(&callable_name)
735                    .ok_or_else(|| GentError::UnknownTool {
736                        name: callable_name.clone(),
737                        span: span.clone(),
738                    })?;
739
740                // Convert arguments to JSON for tool execution
741                let json_args = args_to_json(&arg_values);
742
743                // Execute the tool
744                let result = tool
745                    .execute(json_args)
746                    .await
747                    .map_err(|e| GentError::ToolError {
748                        tool: callable_name.clone(),
749                        message: e,
750                    })?;
751
752                // For now, return the result as a string
753                // TODO: Parse JSON results in the future
754                Ok(Value::String(result))
755            }
756
757            // Match expression
758            Expression::Match(match_expr) => {
759                let subject = evaluate_expr_async(&match_expr.subject, env, tools, ctx).await?;
760
761                for arm in &match_expr.arms {
762                    if let Some(bindings) = match_pattern(&subject, &arm.pattern) {
763                        // Create new scope with bindings
764                        let mut match_env = env.clone();
765                        match_env.push_scope();
766                        for (name, value) in bindings {
767                            match_env.define(&name, value);
768                        }
769
770                        // Evaluate arm body
771                        let result = match &arm.body {
772                            MatchBody::Expression(expr) => {
773                                evaluate_expr_async(expr, &match_env, tools, ctx).await?
774                            }
775                            MatchBody::Block(block) => {
776                                evaluate_block_with_ctx(block, &mut match_env, tools, ctx).await?
777                            }
778                        };
779
780                        return Ok(result);
781                    }
782                }
783
784                // No match found
785                Err(GentError::SyntaxError {
786                    message: "Non-exhaustive match: no pattern matched".to_string(),
787                    span: match_expr.span.clone(),
788                })
789            }
790
791            // All other expressions can be evaluated synchronously
792            _ => evaluate_expr(expr, env),
793        }
794    })
795}
796
797/// Convert a vector of Values to a JSON value for tool execution
798fn args_to_json(args: &[Value]) -> serde_json::Value {
799    use serde_json::{json, Map, Value as JsonValue};
800
801    fn value_to_json(val: &Value) -> JsonValue {
802        match val {
803            Value::String(s) => JsonValue::String(s.clone()),
804            Value::Number(n) => json!(n),
805            Value::Boolean(b) => JsonValue::Bool(*b),
806            Value::Null => JsonValue::Null,
807            Value::Array(items) => {
808                let json_items: Vec<JsonValue> = items.iter().map(value_to_json).collect();
809                JsonValue::Array(json_items)
810            }
811            Value::Object(map) => {
812                let mut json_map = Map::new();
813                for (k, v) in map {
814                    json_map.insert(k.clone(), value_to_json(v));
815                }
816                JsonValue::Object(json_map)
817            }
818            Value::Agent(_) => JsonValue::String("<agent>".to_string()),
819            Value::Tool(_) => JsonValue::String("<tool>".to_string()),
820            Value::Function(_) => JsonValue::String("<function>".to_string()),
821            Value::Lambda(_) => JsonValue::String("<lambda>".to_string()),
822            Value::Enum(e) => {
823                if e.data.is_empty() {
824                    JsonValue::String(format!("{}.{}", e.enum_name, e.variant))
825                } else {
826                    let mut map = Map::new();
827                    map.insert("enum".to_string(), JsonValue::String(e.enum_name.clone()));
828                    map.insert(
829                        "variant".to_string(),
830                        JsonValue::String(e.variant.clone()),
831                    );
832                    let data: Vec<JsonValue> = e.data.iter().map(value_to_json).collect();
833                    map.insert("data".to_string(), JsonValue::Array(data));
834                    JsonValue::Object(map)
835                }
836            }
837            Value::EnumConstructor(c) => {
838                JsonValue::String(format!("<enum constructor {}.{}>", c.enum_name, c.variant))
839            }
840            Value::Parallel(p) => JsonValue::String(format!("<parallel {}>", p.name)),
841        }
842    }
843
844    // If there's a single object argument, use it directly
845    // Otherwise, wrap arguments in an array
846    if args.len() == 1 {
847        if let Value::Object(_) = &args[0] {
848            return value_to_json(&args[0]);
849        }
850    }
851
852    // For multiple args or non-object single arg, create an array
853    JsonValue::Array(args.iter().map(value_to_json).collect())
854}
855
856/// Extract variable name, method name, and arguments from a method call expression
857/// Returns Some((var_name, method_name, args)) if this is a method call on an identifier
858fn extract_array_method_call(expr: &Expression) -> Option<(String, String, &Vec<Expression>)> {
859    if let Expression::Call(callee_expr, args, _) = expr {
860        if let Expression::Member(obj_expr, method_name, _) = callee_expr.as_ref() {
861            if let Expression::Identifier(var_name, _) = obj_expr.as_ref() {
862                return Some((var_name.clone(), method_name.clone(), args));
863            }
864        }
865    }
866    None
867}
868
869/// Match a value against a pattern, returning bindings if successful
870fn match_pattern(value: &Value, pattern: &MatchPattern) -> Option<Vec<(String, Value)>> {
871    match pattern {
872        MatchPattern::Wildcard => Some(vec![]),
873        MatchPattern::EnumVariant { enum_name, variant_name, bindings } => {
874            if let Value::Enum(enum_val) = value {
875                if enum_val.enum_name == *enum_name && enum_val.variant == *variant_name {
876                    // Bind data to pattern variables
877                    let mut result = Vec::new();
878                    for (i, binding) in bindings.iter().enumerate() {
879                        if let Some(data) = enum_val.data.get(i) {
880                            result.push((binding.clone(), data.clone()));
881                        }
882                    }
883                    return Some(result);
884                }
885            }
886            None
887        }
888    }
889}
890
891/// Convert a JSON value to a GENT Value
892fn json_to_value(json: &serde_json::Value) -> Value {
893    match json {
894        serde_json::Value::Null => Value::Null,
895        serde_json::Value::Bool(b) => Value::Boolean(*b),
896        serde_json::Value::Number(n) => {
897            if let Some(f) = n.as_f64() {
898                Value::Number(f)
899            } else {
900                Value::Null
901            }
902        }
903        serde_json::Value::String(s) => Value::String(s.clone()),
904        serde_json::Value::Array(arr) => {
905            let items = arr.iter().map(json_to_value).collect();
906            Value::Array(items)
907        }
908        serde_json::Value::Object(obj) => {
909            let mut map = HashMap::new();
910            for (k, v) in obj {
911                map.insert(k.clone(), json_to_value(v));
912            }
913            Value::Object(map)
914        }
915    }
916}