use const_format::concatcp;
use crate::bug;
use crate::cfg::CfgMod;
use crate::cfg::eval_with_prelude;
use crate::cfg::extend_func;
use crate::semantics::cfg::Cfg;
use crate::semantics::core::Eval;
use crate::semantics::core::PREFIX_CELL;
use crate::semantics::ctx::DynCtx;
use crate::semantics::func::CtxConstInputFreeFunc;
use crate::semantics::func::CtxFreeInputAwareFunc;
use crate::semantics::func::CtxFreeInputFreeFunc;
use crate::semantics::func::CtxMutInputAwareFunc;
use crate::semantics::func::DynFunc;
use crate::semantics::val::CFG;
use crate::semantics::val::PrimFuncVal;
use crate::semantics::val::Val;
use crate::type_::Bit;
use crate::type_::Int;
use crate::type_::Map;
use crate::type_::Pair;
#[derive(Clone)]
pub struct CfgLib {
pub make: PrimFuncVal,
pub represent: PrimFuncVal,
pub exist: PrimFuncVal,
pub import: PrimFuncVal,
pub export: PrimFuncVal,
pub get_length: PrimFuncVal,
pub with: PrimFuncVal,
pub get_self: PrimFuncVal,
pub where_: PrimFuncVal,
}
pub const MAKE: &str = concatcp!(PREFIX_CELL, CFG, ".make");
pub const REPRESENT: &str = concatcp!(PREFIX_CELL, CFG, ".represent");
pub const EXIST: &str = concatcp!(PREFIX_CELL, CFG, ".exist");
pub const IMPORT: &str = concatcp!(PREFIX_CELL, CFG, ".import");
pub const EXPORT: &str = concatcp!(PREFIX_CELL, CFG, ".export");
pub const GET_LENGTH: &str = concatcp!(PREFIX_CELL, CFG, ".get_length");
pub const WITH: &str = concatcp!(PREFIX_CELL, CFG, ".with");
pub const GET_SELF: &str = concatcp!(PREFIX_CELL, CFG, ".get_self");
pub const WHERE: &str = concatcp!(PREFIX_CELL, CFG, ".where");
impl Default for CfgLib {
fn default() -> Self {
CfgLib {
make: CtxFreeInputAwareFunc { fn_: make }.build(),
represent: CtxFreeInputAwareFunc { fn_: represent }.build(),
exist: CtxFreeInputAwareFunc { fn_: exist }.build(),
import: CtxFreeInputAwareFunc { fn_: import }.build(),
export: CtxFreeInputAwareFunc { fn_: export }.build(),
get_length: CtxConstInputFreeFunc { fn_: get_length }.build(),
with: CtxMutInputAwareFunc { fn_: with }.build(),
get_self: CtxFreeInputFreeFunc { fn_: get_self }.build(),
where_: CtxMutInputAwareFunc { fn_: where_ }.build(),
}
}
}
impl CfgMod for CfgLib {
fn extend(self, cfg: &mut Cfg) {
extend_func(cfg, MAKE, self.make);
extend_func(cfg, REPRESENT, self.represent);
extend_func(cfg, EXIST, self.exist);
extend_func(cfg, IMPORT, self.import);
extend_func(cfg, EXPORT, self.export);
extend_func(cfg, GET_LENGTH, self.get_length);
extend_func(cfg, WITH, self.with);
extend_func(cfg, GET_SELF, self.get_self);
extend_func(cfg, WHERE, self.where_);
}
}
pub fn make(cfg: &mut Cfg, input: Val) -> Val {
let Val::Map(map) = input else {
return bug!(cfg, "{MAKE}: expected input to be a map, but got {input}");
};
Val::Cfg(Cfg::from(Map::from(map)).into())
}
pub fn represent(cfg: &mut Cfg, input: Val) -> Val {
let Val::Cfg(new_cfg) = input else {
return bug!(cfg, "{REPRESENT}: expected input to be a config, but got {input}");
};
Val::Map(Map::from(Cfg::from(new_cfg)).into())
}
pub fn exist(cfg: &mut Cfg, input: Val) -> Val {
let Val::Key(name) = input else {
return bug!(cfg, "{EXIST}: expected input to be a key, but got {input}");
};
let exist = cfg.contains_key(&name);
Val::Bit(Bit::from(exist))
}
pub fn import(cfg: &mut Cfg, input: Val) -> Val {
let Val::Key(name) = input else {
return bug!(cfg, "{IMPORT}: expected input to be a key, but got {input}");
};
let Some(value) = cfg.get(&name) else {
return bug!(cfg, "{IMPORT}: value not found for key {name} in config");
};
value.clone()
}
pub fn export(cfg: &mut Cfg, input: Val) -> Val {
let Val::Pair(pair) = input else {
return bug!(cfg, "{EXPORT}: expected input to be a pair, but got {input}");
};
let pair = Pair::from(pair);
let Val::Key(name) = pair.left else {
return bug!(cfg, "{EXPORT}: expected input.left to be a key, but got {}", pair.left);
};
if cfg.contains_key(&name) {
return bug!(cfg, "{EXPORT}: already bound to value for key {name} in config");
}
cfg.insert(name, pair.right);
Val::default()
}
pub fn get_length(cfg: &mut Cfg, ctx: &Val) -> Val {
let Val::Cfg(new_cfg) = ctx else {
return bug!(cfg, "{GET_LENGTH}: expected context to be a config, but got {ctx}");
};
Val::Int(Int::from(new_cfg.len()).into())
}
pub fn with(cfg: &mut Cfg, ctx: &mut Val, input: Val) -> Val {
let Val::Pair(pair) = input else {
return bug!(cfg, "{WITH}: expected input to be a pair, but got {input}");
};
let pair = Pair::from(pair);
let map = pair.left;
let Val::Map(map) = map else {
return bug!(cfg, "{WITH}: expected input.left to be a map, but got {map}");
};
let mut backup = Map::with_capacity(map.len());
for (k, v) in Map::from(map) {
backup.insert(k.clone(), cfg.insert(k, v));
}
let output = Eval.call(cfg, ctx, pair.right);
for (k, v) in backup {
if let Some(v) = v {
cfg.insert(k, v);
} else {
cfg.remove(&k);
}
}
output
}
pub fn get_self(cfg: &mut Cfg) -> Val {
Val::Cfg(cfg.clone().into())
}
pub fn where_(cfg: &mut Cfg, ctx: &mut Val, input: Val) -> Val {
let Val::Pair(pair) = input else {
return bug!(cfg, "{WHERE}: expected input to be a pair, but got {input}");
};
let pair = Pair::from(pair);
let Some(ctx) = ctx.ref_mut(cfg, pair.left.clone()) else {
return Val::default();
};
let Val::Cfg(new_cfg) = ctx else {
return bug!(cfg, "{WHERE}: expected context to be a config, but got {ctx}");
};
eval_with_prelude(new_cfg, WHERE, pair.right)
}