use super::*;

impl SymExecutor {
    pub(crate) fn eval_expr(&mut self, expr: &Expr) -> SymValue {
        match expr {
            Expr::Literal(Literal::Integer(n)) => SymValue::Const(*n),
            Expr::Literal(Literal::Bool(b)) => SymValue::Const(if *b { 1 } else { 0 }),
            Expr::Var(name) => {
                self.env.get(name).cloned().unwrap_or_else(|| {
                    // Unknown variable โ€” treat as fresh symbolic
                    let var = self.fresh_var(name);
                    SymValue::Var(var)
                })
            }
            Expr::BinOp { op, lhs, rhs } => {
                let l = self.eval_expr(&lhs.node);
                let r = self.eval_expr(&rhs.node);
                match op {
                    BinOp::Add => SymValue::Add(Box::new(l), Box::new(r)).simplify(),
                    BinOp::Mul => SymValue::Mul(Box::new(l), Box::new(r)).simplify(),
                    BinOp::Eq => SymValue::Eq(Box::new(l), Box::new(r)).simplify(),
                    BinOp::Lt => SymValue::Lt(Box::new(l), Box::new(r)),
                    _ => {
                        // BitAnd, BitXor, DivMod, XFieldMul โ€” leave as opaque
                        SymValue::Var(self.fresh_var("__binop"))
                    }
                }
            }
            Expr::Call { path, args, .. } => self.eval_call(&path.node, args),
            Expr::Tuple(elems) => {
                // Tuples are represented as the first element for simplicity.
                // Full tuple tracking would require a SymValue::Tuple variant.
                if elems.len() == 1 {
                    self.eval_expr(&elems[0].node)
                } else {
                    // Create fresh variables for each tuple element
                    let var = self.fresh_var("__tuple");
                    SymValue::Var(var)
                }
            }
            Expr::FieldAccess { expr, .. } => {
                let _ = self.eval_expr(&expr.node);
                let var = self.fresh_var("__field");
                SymValue::Var(var)
            }
            Expr::Index { expr, .. } => {
                let _ = self.eval_expr(&expr.node);
                let var = self.fresh_var("__index");
                SymValue::Var(var)
            }
            Expr::StructInit { fields, .. } => {
                for (_, val) in fields {
                    let _ = self.eval_expr(&val.node);
                }
                let var = self.fresh_var("__struct");
                SymValue::Var(var)
            }
            Expr::ArrayInit(elems) => {
                for e in elems {
                    let _ = self.eval_expr(&e.node);
                }
                let var = self.fresh_var("__array");
                SymValue::Var(var)
            }
        }
    }

    /// Evaluate a function call (builtin or user-defined).
    pub(crate) fn eval_call(&mut self, path: &ModulePath, args: &[Spanned<Expr>]) -> SymValue {
        let name = path.as_dotted();
        let func_name = path.0.last().map(|s| s.as_str()).unwrap_or("");

        // Handle builtins
        match func_name {
            "pub_read" | "read" => return self.fresh_pub_input(),
            "pub_read2" | "read2" => {
                self.fresh_pub_input();
                return self.fresh_pub_input();
            }
            "pub_read5" | "read5" => {
                for _ in 0..5 {
                    self.fresh_pub_input();
                }
                let var = self.fresh_var("__digest");
                return SymValue::Var(var);
            }
            "pub_write" | "write" => {
                if let Some(arg) = args.first() {
                    let val = self.eval_expr(&arg.node);
                    self.system.pub_outputs.push(val);
                }
                return SymValue::Const(0);
            }
            "divine" => return self.fresh_divine(),
            "divine3" => {
                for _ in 0..3 {
                    self.fresh_divine();
                }
                let var = self.fresh_var("__divine3");
                return SymValue::Var(var);
            }
            "divine5" => {
                for _ in 0..5 {
                    self.fresh_divine();
                }
                let var = self.fresh_var("__divine5");
                return SymValue::Var(var);
            }
            "hash" | "tip5" => {
                let inputs: Vec<SymValue> = args.iter().map(|a| self.eval_expr(&a.node)).collect();
                return SymValue::Hash(inputs, 0);
            }
            "assert" => {
                if let Some(arg) = args.first() {
                    let val = self.eval_expr(&arg.node);
                    self.add_constraint(Constraint::AssertTrue(val));
                }
                return SymValue::Const(0);
            }
            "assert_eq" | "eq" => {
                if args.len() >= 2 {
                    let a = self.eval_expr(&args[0].node);
                    let b = self.eval_expr(&args[1].node);
                    self.add_constraint(Constraint::Equal(a, b));
                }
                return SymValue::Const(0);
            }
            "assert_digest" | "digest" => {
                // Digest equality: 5-element vector comparison
                if args.len() >= 2 {
                    let a = self.eval_expr(&args[0].node);
                    let b = self.eval_expr(&args[1].node);
                    self.add_constraint(Constraint::Equal(a, b));
                }
                return SymValue::Const(0);
            }
            "as_u32" => {
                if let Some(arg) = args.first() {
                    let val = self.eval_expr(&arg.node);
                    self.add_constraint(Constraint::RangeU32(val.clone()));
                    return val;
                }
                return SymValue::Const(0);
            }
            "as_field" => {
                // Type conversion: U32 โ†’ Field (identity in the field)
                if let Some(arg) = args.first() {
                    return self.eval_expr(&arg.node);
                }
                return SymValue::Const(0);
            }
            "sub" => {
                if args.len() >= 2 {
                    let a = self.eval_expr(&args[0].node);
                    let b = self.eval_expr(&args[1].node);
                    return SymValue::Sub(Box::new(a), Box::new(b)).simplify();
                }
                return SymValue::Const(0);
            }
            "neg" => {
                if let Some(arg) = args.first() {
                    let val = self.eval_expr(&arg.node);
                    return SymValue::Neg(Box::new(val)).simplify();
                }
                return SymValue::Const(0);
            }
            "inv" => {
                if let Some(arg) = args.first() {
                    let val = self.eval_expr(&arg.node);
                    return SymValue::Inv(Box::new(val));
                }
                return SymValue::Const(0);
            }
            _ => {}
        }

        // Try user-defined function inlining
        if self.call_depth < self.max_call_depth {
            // Look up the function: try full path first, then last component
            let func = self
                .functions
                .get(&name)
                .cloned()
                .or_else(|| self.functions.get(func_name).cloned());

            if let Some(func) = func {
                if let Some(ref body) = func.body {
                    self.call_depth += 1;
                    let saved_env = self.env.clone();

                    // Bind parameters
                    for (param, arg) in func.params.iter().zip(args.iter()) {
                        let val = self.eval_expr(&arg.node);
                        self.env.insert(param.name.node.clone(), val);
                    }

                    // Execute function body
                    self.execute_block(&body.node);

                    // Restore environment (except new constraints are kept)
                    self.env = saved_env;
                    self.call_depth -= 1;
                }
            }
        }

        // Default: return a fresh symbolic variable
        let var = self.fresh_var(&format!("__call_{}", func_name));
        SymValue::Var(var)
    }

    /// Project element `i` from a tuple-like symbolic value.
    pub(crate) fn project_tuple(&mut self, val: &SymValue, i: usize) -> SymValue {
        // If projecting from a hash, preserve the Hash origin with the index
        if let SymValue::Hash(inputs, _) = val {
            return SymValue::Hash(inputs.clone(), i);
        }
        let var = self.fresh_var(&format!("__proj_{}", i));
        SymValue::Var(var)
    }
}

Dimensions

trident/src/typecheck/expr.rs
trident/src/syntax/parser/expr.rs
trident/src/syntax/format/expr.rs
trident/src/ir/tir/builder/expr.rs

Local Graph