ravenlang 0.5.0

Language core for ravencheck.
Documentation
use crate::{
    Binder1,
    BinderN,
    Builder,
    Call,
    Comp,
    IGen,
    LogOpN,
    Oc,
    Op,
    Quantifier,
    Rebuild,
    Sig,
    Ident,
    VType,
    Val,
    FunOp,
    PredOp,
};

fn expand_fun(op: FunOp, vs: Vec<Val>, qm: Quantifier, xs: Vec<Ident>, m: Comp, sig: &Sig, igen: &mut IGen) -> Comp {
    assert!(
        !op.output.contains_prop(),
        "Can't have bool-output primitive function. Define a predicate instead."
    );
    let flat_output = op.output.flatten();
    assert!(
        xs.len() == flat_output.len(),
        "expand_fun mismatch between var-count and output-type-count",
    );
    let output_tuple = Val::tuple(
        xs.clone()
            .into_iter()
            .map(|x| x.val())
            .collect()
    );
    let q_sig = xs.into_iter().zip(flat_output).collect();

    match qm {
        Quantifier::Exists => {
            let mut conjuncts: Vec<Builder> = op.axioms.into_iter().map(|axiom| {
                Builder::new(|igen| axiom.rename(igen))
                    .flatten()
                    .apply_v(vs.clone())
                    .flatten()
                    .apply_v(vec![output_tuple.clone()])
            }).collect();
            conjuncts.push(Builder::lift(m));
            let new_body = Builder::log_op(LogOpN::And, conjuncts)
                .into_def_and()
                .build_with(igen);
        
            let x_result = igen.next();
            let new_m =
                Comp::quant_many(
                    Quantifier::Exists,
                    q_sig,
                    new_body.normal_form_single_case(sig,igen),
                    x_result.clone(),
                    Comp::return1(x_result),
                );
            new_m
        }
        Quantifier::Forall => {
            let mut disjuncts: Vec<Builder> = op.axioms.into_iter().map(|axiom| {
                Builder::new(|igen| axiom.rename(igen))
                    .flatten()
                    .apply_v(vs.clone())
                    .flatten()
                    .apply_v(vec![output_tuple.clone()])
                    .not()
            }).collect();
            disjuncts.push(Builder::lift(m));
            let new_body = Builder::log_op(LogOpN::Or, disjuncts)
                .into_undef_or()
                .build_with(igen);
        
            let x_result = igen.next();
            let new_m =
                Comp::quant_many(
                    Quantifier::Forall,
                    q_sig,
                    new_body.normal_form_single_case(sig,igen),
                    x_result.clone(),
                    Comp::return1(x_result),
                );
            new_m
        }
    }
}

#[allow(dead_code)]
fn expand_pred(op: PredOp, vs: Vec<Val>, x: Ident, m: Comp, sig: &Sig, igen: &mut IGen, is_pos: bool) -> Comp {
    let sig_clone1 = sig.clone();
    let sig_clone2 = sig.clone();

    // Build the POSITIVE branch: one big disjunction. SOME axiom is
    // FALSE, OR the condition with 'true' plugged in is TRUE.
    //
    // !axiom_0 OR ... OR !axiom_n OR condition[true]
    let mut t_disjuncts: Vec<Builder> =
        op.axioms.clone().into_iter().map(|axiom| {
            Builder::new(|igen| axiom.rename(igen))
                .flatten()
                .apply_v(vs.clone())
                .not()
        })
        .collect();
    let t_val = Val::from_bool(is_pos);
    t_disjuncts.push(
        Builder::lift(m.clone().substitute(&x, &t_val))
    );
    let pos_branch = Builder::new(move |igen| {
        Builder::log_op(LogOpN::Or, t_disjuncts)
            .build_with(igen)
            .normal_form_single_case(&sig_clone1, igen)
    });

    // Build the FALSE branch: disjunction with a conjunction. ALL
    // axioms are TRUE, OR the condition with 'false' plugged in is
    // TRUE.
    //
    // (axiom_0 AND ... AND axiom_n) OR condition[false]
    //
    // Note that if there are no axioms, this collapses to simply:
    //
    // true
    //
    // We use if-then-else for this case below, because easy_smt
    // panics when you construct an AND_MANY node with no arguments.
    let f_conjuncts: Vec<Builder> =
        op.axioms.clone().into_iter().map(|axiom| {
            Builder::new(|igen| axiom.rename(igen))
                .flatten()
                .apply_v(vs.clone())
        })
        .collect();
    let f_val = Val::from_bool(!is_pos);
    let neg_branch = if f_conjuncts.len() > 0 {
        Builder::new(move |igen| {
            Builder::log_op(LogOpN::Or, [
                Builder::log_op(LogOpN::And, f_conjuncts),
                Builder::lift(m.clone().substitute(&x, &f_val))
            ])
                .build_with(igen)
                .normal_form_single_case(&sig_clone2, igen)
            })
    } else {
        Builder::return_(Val::true_())
    };
    
    Builder::log_op(LogOpN::And, [
        pos_branch,
        neg_branch,
    ])
        .build_with(igen)
        .normal_form_single_case(sig,igen)
}

impl Comp {
    pub fn expand_funs(mut self, sig: &Sig, igen: &mut IGen, mut anti_stack: Vec<Rebuild>, qmode: Quantifier) -> Self {
        loop {
            match self {
                Self::Bind1(b, x, m) => {
                    match b {
                        Binder1::LogOpN(LogOpN::Pred(oc,is_pos), vs) => {
                            // anti_stack.push(Rebuild::LogOpN(LogOpN::Pred(oc, is_pos), vs, x));
                            // self = *m;
                            match sig.get_applied_op_or_con(&oc).unwrap() {
                                Oc::Op(Op::Const(..)) => {
                                    panic!("Got constant op {:?} in Pred", oc)
                                }
                                Oc::Op(Op::Direct(..)) => {
                                    panic!("Got direct fun {:?} in Pred", oc)
                                }
                                Oc::Op(Op::Pred(op)) => {
                                    // println!("Expanding pred {}...", &oc.ident);
                                    self = expand_pred(op.clone(), vs, x, *m, sig, igen, is_pos);
                                    break;
                                }
                                // Treat Fun like a Symbol, since this
                                // is the relational abstraction being
                                // applied.
                                Oc::Op(Op::Fun(..)) => {
                                    anti_stack.push(Rebuild::LogOpN(
                                        LogOpN::Pred(oc,is_pos),
                                        vs,
                                        x,
                                    ));
                                    self = *m;
                                }
                                // Same deal for Rec
                                Oc::Op(Op::Rec(..)) => {
                                    anti_stack.push(Rebuild::LogOpN(
                                        LogOpN::Pred(oc,is_pos),
                                        vs,
                                        x,
                                    ));
                                    self = *m;
                                }
                                Oc::Op(Op::Symbol(_op)) => {
                                    anti_stack.push(Rebuild::LogOpN(
                                        LogOpN::Pred(oc,is_pos),
                                        vs,
                                        x,
                                    ));
                                    self = *m;
                                }
                                Oc::Con(..) => {
                                    anti_stack.push(Rebuild::LogOpN(
                                        LogOpN::Pred(oc,is_pos),
                                        vs,
                                        x,
                                    ));
                                    self = *m;
                                }
                            }
                        }
                        Binder1::LogQuantifier(q, xs, body) => {
                            let body = body.expand_funs(sig,igen,Vec::new(),qmode);
                            anti_stack.push(Rebuild::Quantifier(q, xs, body, x));
                            self = *m;
                        },
                        Binder1::QMode(new_qmode, body) => {
                            let body = body
                                .expand_funs(sig,igen,Vec::new(),new_qmode);
                            self = Comp::seq(body, x, *m)
                                .partial_eval_single_case(sig, igen);
                            // todo!("Erase the QMode in expand_funs")
                        }
                        b => {
                            anti_stack.push(Rebuild::Bind1(b, x));
                            self = *m;
                        }
                    }
                }
                Self::BindN(BinderN::Call(call), ps, m) => {
                    let Call{code: oc, args: vs, ..} = call;
                    let xs = ps
                        .into_iter()
                        .map(|p| p.unwrap_atom().expect("Call should only be bound to flat patterns"))
                        .collect();
                    match sig.get_applied_op_or_con(&oc).unwrap() {
                        Oc::Con(inputs) => {
                            let output = VType::Base(oc.get_enum_type().unwrap());
                            let axiom = Sig::relabs_axiom(
                                oc.clone(),
                                inputs.clone(),
                                output.clone(),
                            );
                            let op = FunOp{inputs, output, axioms: vec![axiom]};
                            // println!("Expanding call {}...", &oc);
                            self = expand_fun(op, vs, qmode, xs, *m, sig, igen);
                        }
                        Oc::Op(Op::Fun(op)) => {
                            // println!("Expanding call {}...", &oc);
                            self = expand_fun(op, vs, qmode, xs, *m, sig, igen);
                            break;
                        }
                        Oc::Op(Op::Pred(_op)) => {
                            panic!("Got pred op {:?} in Fun", oc)
                        }
                        Oc::Op(Op::Rec(op)) => {
                            // println!("Expanding call {}...", &oc);
                            self = expand_fun(op.as_fun_op(), vs, qmode, xs, *m, sig, igen);
                            break;
                        }
                        r => panic!("Can't expand_fun on {:?}", r),
                    }
                }
                Self::Ite(cond, then_b, else_b) => {
                    let then_b = then_b.expand_funs(sig,igen,Vec::new(),qmode);
                    let else_b = else_b.expand_funs(sig,igen,Vec::new(),qmode);
                    self = Self::ite(cond, then_b, else_b);
                    break;
                }
                Self::Return(vs) => {
                    self = Self::Return(vs);
                    break;
                }
                m => panic!("expand_funs: Unexpected Comp {:?}", m),
            }
        }
        self.rebuild_from_stack(anti_stack)
    }
}