airlang 0.26.0

Air is a minimalist and universal programming language.
Documentation
use const_format::concatcp;

use crate::bug;
use crate::cfg::CfgMod;
use crate::cfg::extend_func;
use crate::semantics::cfg::Cfg;
use crate::semantics::core::PREFIX_CELL;
use crate::semantics::func::CtxFreeInputAwareFunc;
use crate::semantics::val::PrimFuncVal;
use crate::semantics::val::Val;
use crate::type_::Bit;
use crate::type_::Cell;
use crate::type_::Pair;

#[derive(Copy, Clone)]
pub struct FactLib {
    pub put: PrimFuncVal,
    pub call: PrimFuncVal,
    pub solve: PrimFuncVal,
    pub exist: PrimFuncVal,
}

const FACT: &str = "fact";

pub const PUT: &str = concatcp!(PREFIX_CELL, FACT, ".put");
pub const CALL: &str = concatcp!(PREFIX_CELL, FACT, ".call");
pub const SOLVE: &str = concatcp!(PREFIX_CELL, FACT, ".solve");
pub const EXIST: &str = concatcp!(PREFIX_CELL, FACT, ".exist");

impl Default for FactLib {
    fn default() -> Self {
        Self {
            put: CtxFreeInputAwareFunc { fn_: put }.build(),
            call: CtxFreeInputAwareFunc { fn_: call }.build(),
            solve: CtxFreeInputAwareFunc { fn_: solve }.build(),
            exist: CtxFreeInputAwareFunc { fn_: exist }.build(),
        }
    }
}

impl CfgMod for FactLib {
    fn extend(self, cfg: &mut Cfg) {
        extend_func(cfg, PUT, self.put);
        extend_func(cfg, CALL, self.call);
        extend_func(cfg, SOLVE, self.solve);
        extend_func(cfg, EXIST, self.exist);
    }
}

pub fn put(cfg: &mut Cfg, input: Val) -> Val {
    let Val::Pair(pair) = input else {
        return bug!(cfg, "{PUT}: expected input to be a pair, but got {input}");
    };
    let pair = Pair::from(pair);
    let Val::Func(func) = pair.left else {
        return bug!(cfg, "{PUT}: expected input.left to be a function, but got {}", pair.left);
    };
    cfg.fact_put(&mut Val::default(), func, pair.right);
    Val::default()
}

pub fn call(cfg: &mut Cfg, input: Val) -> Val {
    let Val::Pair(pair) = input else {
        return bug!(cfg, "{CALL}: expected input to be a pair, but got {input}");
    };
    let pair = Pair::from(pair);
    let Val::Func(func) = pair.left else {
        return bug!(cfg, "{CALL}: expected input.left to be a function, but got {}", pair.left);
    };
    match cfg.fact_call(func, pair.right) {
        Some(output) => Val::Cell(Cell::new(output).into()),
        None => Val::default(),
    }
}

pub fn solve(cfg: &mut Cfg, input: Val) -> Val {
    let Val::Pair(pair) = input else {
        return bug!(cfg, "{SOLVE}: expected input to be a pair, but got {input}");
    };
    let pair = Pair::from(pair);
    let Val::Func(func) = pair.left else {
        return bug!(cfg, "{SOLVE}: expected input.left to be a function, but got {}", pair.left);
    };
    match cfg.fact_solve(func, pair.right) {
        Some(input) => Val::Cell(Cell::new(input).into()),
        None => Val::default(),
    }
}

pub fn exist(cfg: &mut Cfg, input: Val) -> Val {
    let Val::Pair(pair) = input else {
        return bug!(cfg, "{EXIST}: expected input to be a pair, but got {input}");
    };
    let pair = Pair::from(pair);
    let Val::Func(func) = pair.left else {
        return bug!(cfg, "{EXIST}: expected input.left to be a function, but got {}", pair.left);
    };
    let Val::Pair(pair) = pair.right else {
        return bug!(cfg, "{EXIST}: expected input.right to be a pair, but got {}", pair.right);
    };
    let pair = Pair::from(pair);
    let exist = cfg.fact_exist(func, pair.left, pair.right);
    Val::Bit(Bit::from(exist))
}