use std::ops::Deref;
use std::panic::AssertUnwindSafe;
use std::panic::catch_unwind;
use const_format::concatcp;
use super::ConstImpl;
use super::DynImpl;
use super::FreeImpl;
use super::MutImpl;
use super::abort_const;
use super::abort_free;
use crate::bug;
use crate::cfg::CfgMod;
use crate::cfg::CoreCfg;
use crate::cfg::extend_func;
use crate::semantics::cfg::Cfg;
use crate::semantics::core::Eval;
use crate::semantics::core::PREFIX_ID;
use crate::semantics::ctx::DynCtx;
use crate::semantics::func::MutFn;
use crate::semantics::val::CFG;
use crate::semantics::val::ConstPrimFuncVal;
use crate::semantics::val::FreePrimFuncVal;
use crate::semantics::val::MutPrimFuncVal;
use crate::semantics::val::Val;
use crate::type_::Bit;
use crate::type_::ConstRef;
use crate::type_::DynRef;
use crate::type_::Int;
use crate::type_::Key;
use crate::type_::Map;
use crate::type_::Pair;
#[derive(Clone)]
pub struct CfgLib {
pub new: FreePrimFuncVal,
pub represent: FreePrimFuncVal,
pub exist: FreePrimFuncVal,
pub import: FreePrimFuncVal,
pub export: FreePrimFuncVal,
pub get_length: ConstPrimFuncVal,
pub with: MutPrimFuncVal,
pub self_: FreePrimFuncVal,
pub where_: MutPrimFuncVal,
}
pub const NEW: &str = concatcp!(PREFIX_ID, CFG, ".new");
pub const REPRESENT: &str = concatcp!(PREFIX_ID, CFG, ".represent");
pub const EXIST: &str = concatcp!(PREFIX_ID, CFG, ".exist");
pub const IMPORT: &str = concatcp!(PREFIX_ID, CFG, ".import");
pub const EXPORT: &str = concatcp!(PREFIX_ID, CFG, ".export");
pub const GET_LENGTH: &str = concatcp!(PREFIX_ID, CFG, ".get_length");
pub const WITH: &str = concatcp!(PREFIX_ID, CFG, ".with");
pub const SELF: &str = concatcp!(PREFIX_ID, CFG, ".self");
pub const WHERE: &str = concatcp!(PREFIX_ID, CFG, ".where");
impl Default for CfgLib {
fn default() -> Self {
CfgLib {
new: new(),
represent: represent(),
exist: exist(),
import: import(),
export: export(),
get_length: get_length(),
with: with(),
self_: self_(),
where_: where_(),
}
}
}
impl CfgMod for CfgLib {
fn extend(self, cfg: &Cfg) {
extend_func(cfg, NEW, self.new);
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, SELF, self.self_);
extend_func(cfg, WHERE, self.where_);
}
}
pub fn new() -> FreePrimFuncVal {
FreeImpl { free: fn_new }.build()
}
fn fn_new(cfg: &mut Cfg, input: Val) -> Val {
let Val::Map(map) = input else {
return bug!(cfg, "{NEW}: expected input to be a map, but got {input}");
};
let new_cfg = Cfg::default();
let map = Map::from(map);
for (k, v) in map {
new_cfg.extend_scope(k, v);
}
Val::Cfg(new_cfg.into())
}
pub fn represent() -> FreePrimFuncVal {
FreeImpl { free: fn_represent }.build()
}
fn 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(new_cfg.snapshot().into())
}
pub fn exist() -> FreePrimFuncVal {
FreeImpl { free: fn_exist }.build()
}
fn 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.exist(name);
Val::Bit(Bit::from(exist))
}
pub fn import() -> FreePrimFuncVal {
FreeImpl { free: fn_import }.build()
}
fn 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.import(name.clone()) else {
return bug!(cfg, "{IMPORT}: value not found for key {name} in config");
};
value
}
pub fn export() -> FreePrimFuncVal {
FreeImpl { free: fn_export }.build()
}
fn 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.export(name.clone(), pair.right).is_none() {
return bug!(cfg, "{EXPORT}: already bound to value for key {name} in config");
}
Val::default()
}
pub fn get_length() -> ConstPrimFuncVal {
ConstImpl { free: abort_free(GET_LENGTH), const_: fn_get_length }.build()
}
fn fn_get_length(cfg: &mut Cfg, ctx: ConstRef<Val>, input: Val) -> Val {
let Val::Cfg(new_cfg) = &*ctx else {
return bug!(cfg, "{GET_LENGTH}: expected context to be a config, but got {}", ctx.deref());
};
if !input.is_unit() {
return bug!(cfg, "{GET_LENGTH}: expected input to be a unit, but got {input}");
}
Val::Int(Int::from(new_cfg.len()).into())
}
pub fn with() -> MutPrimFuncVal {
DynImpl { free: abort_free(WITH), dyn_: fn_with }.build_with(true)
}
fn fn_with(cfg: &mut Cfg, mut ctx: DynRef<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 = Eval.dyn_call(cfg, ctx.reborrow(), pair.left);
let Val::Map(map) = map else {
return bug!(cfg, "{WITH}: expected input.left to be a map, but got {map}");
};
cfg.begin_scope();
let map = Map::from(map);
for (k, v) in map {
cfg.extend_scope(k, v);
}
let output = Eval.dyn_call(cfg, ctx, pair.right);
cfg.end_scope();
output
}
pub fn self_() -> FreePrimFuncVal {
FreeImpl { free: fn_self }.build()
}
fn fn_self(cfg: &mut Cfg, input: Val) -> Val {
if !input.is_unit() {
return bug!(cfg, "{SELF}: expected input to be a unit, but got {input}");
}
Val::Cfg(cfg.clone().into())
}
pub fn where_() -> MutPrimFuncVal {
MutImpl { free: abort_free(WHERE), const_: abort_const(WHERE), mut_: fn_where }.build_with(true)
}
fn 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}");
};
let prelude = new_cfg.import(Key::from_str_unchecked(CoreCfg::PRELUDE));
let Some(prelude) = prelude else {
return bug!(cfg, "{WHERE}: value not found for key {} in config", CoreCfg::PRELUDE);
};
let Val::Link(prelude) = prelude else {
return bug!(cfg, "{WHERE}: expected {} to be a link, but got {prelude}", CoreCfg::PRELUDE);
};
let Ok(prelude) = prelude.try_borrow() else {
return bug!(cfg, "{WHERE}: link is in use");
};
let mut new_ctx = prelude.deref().clone();
let result =
catch_unwind(AssertUnwindSafe(|| Eval.mut_call(&mut **new_cfg, &mut new_ctx, pair.right)));
match result {
Ok(output) => output,
Err(err) => {
if let Some(err) = err.downcast_ref::<String>() {
bug!(new_cfg, "{WHERE}: panic by {err}")
} else if let Some(err) = err.downcast_ref::<&str>() {
bug!(new_cfg, "{WHERE}: panic by {err}")
} else {
bug!(new_cfg, "{WHERE}: panic")
}
}
}
}