airlang 0.25.0

Air is a minimalist and universal programming language.
Documentation
use std::mem::swap;

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::CtxConstInputAwareFunc;
use crate::semantics::func::CtxConstInputFreeFunc;
use crate::semantics::func::CtxFreeInputAwareFunc;
use crate::semantics::func::CtxMutInputAwareFunc;
use crate::semantics::func::CtxMutInputFreeFunc;
use crate::semantics::val::MAP;
use crate::semantics::val::PrimFuncVal;
use crate::semantics::val::Val;
use crate::type_::Bit;
use crate::type_::Cell;
use crate::type_::Int;
use crate::type_::Key;
use crate::type_::List;
use crate::type_::Map;
use crate::type_::Pair;

// todo design
#[derive(Clone)]
pub struct MapLib {
    pub make: PrimFuncVal,
    pub make_set: PrimFuncVal,
    pub get_length: PrimFuncVal,
    pub get_items: PrimFuncVal,
    pub into_items: PrimFuncVal,
    pub get_keys: PrimFuncVal,
    pub into_keys: PrimFuncVal,
    pub get_values: PrimFuncVal,
    pub into_values: PrimFuncVal,
    pub contain: PrimFuncVal,
    pub contain_all: PrimFuncVal,
    pub contain_any: PrimFuncVal,
    pub set: PrimFuncVal,
    pub set_many: PrimFuncVal,
    pub get: PrimFuncVal,
    pub get_many: PrimFuncVal,
    pub remove: PrimFuncVal,
    pub remove_many: PrimFuncVal,
    pub move_: PrimFuncVal,
    pub clear: PrimFuncVal,
}

pub const MAKE: &str = concatcp!(PREFIX_CELL, MAP, ".make");
pub const MAKE_SET: &str = concatcp!(PREFIX_CELL, MAP, ".make_set");
pub const GET_LENGTH: &str = concatcp!(PREFIX_CELL, MAP, ".get_length");
pub const GET_ITEMS: &str = concatcp!(PREFIX_CELL, MAP, ".get_items");
pub const INTO_ITEMS: &str = concatcp!(PREFIX_CELL, MAP, ".into_items");
pub const GET_KEYS: &str = concatcp!(PREFIX_CELL, MAP, ".get_keys");
pub const INTO_KEYS: &str = concatcp!(PREFIX_CELL, MAP, ".into_keys");
pub const GET_VALUES: &str = concatcp!(PREFIX_CELL, MAP, ".get_values");
pub const INTO_VALUES: &str = concatcp!(PREFIX_CELL, MAP, ".into_values");
pub const CONTAIN: &str = concatcp!(PREFIX_CELL, MAP, ".contain");
pub const CONTAIN_ALL: &str = concatcp!(PREFIX_CELL, MAP, ".contain_all");
pub const CONTAIN_ANY: &str = concatcp!(PREFIX_CELL, MAP, ".contain_any");
pub const SET: &str = concatcp!(PREFIX_CELL, MAP, ".set");
pub const SET_MANY: &str = concatcp!(PREFIX_CELL, MAP, ".set_many");
pub const GET: &str = concatcp!(PREFIX_CELL, MAP, ".get");
pub const GET_MANY: &str = concatcp!(PREFIX_CELL, MAP, ".get_many");
pub const REMOVE: &str = concatcp!(PREFIX_CELL, MAP, ".remove");
pub const REMOVE_MANY: &str = concatcp!(PREFIX_CELL, MAP, ".remove_many");
pub const MOVE: &str = concatcp!(PREFIX_CELL, MAP, ".move");
pub const CLEAR: &str = concatcp!(PREFIX_CELL, MAP, ".clear");

impl Default for MapLib {
    fn default() -> Self {
        MapLib {
            make: CtxFreeInputAwareFunc { fn_: make }.build(),
            make_set: CtxFreeInputAwareFunc { fn_: make_set }.build(),
            get_length: CtxConstInputFreeFunc { fn_: get_length }.build(),
            get_items: CtxConstInputFreeFunc { fn_: get_items }.build(),
            into_items: CtxMutInputFreeFunc { fn_: into_items }.build(),
            get_keys: CtxConstInputFreeFunc { fn_: get_keys }.build(),
            into_keys: CtxMutInputFreeFunc { fn_: into_keys }.build(),
            get_values: CtxConstInputFreeFunc { fn_: get_values }.build(),
            into_values: CtxMutInputFreeFunc { fn_: into_values }.build(),
            contain: CtxConstInputAwareFunc { fn_: contain }.build(),
            contain_all: CtxConstInputAwareFunc { fn_: contain_all }.build(),
            contain_any: CtxConstInputAwareFunc { fn_: contain_any }.build(),
            set: CtxMutInputAwareFunc { fn_: set }.build(),
            set_many: CtxMutInputAwareFunc { fn_: set_many }.build(),
            get: CtxConstInputAwareFunc { fn_: get }.build(),
            get_many: CtxConstInputAwareFunc { fn_: get_many }.build(),
            remove: CtxMutInputAwareFunc { fn_: remove }.build(),
            remove_many: CtxMutInputAwareFunc { fn_: remove_many }.build(),
            move_: CtxMutInputAwareFunc { fn_: move_ }.build(),
            clear: CtxMutInputFreeFunc { fn_: clear }.build(),
        }
    }
}

impl CfgMod for MapLib {
    fn extend(self, cfg: &mut Cfg) {
        extend_func(cfg, MAKE, self.make);
        extend_func(cfg, MAKE_SET, self.make_set);
        extend_func(cfg, GET_LENGTH, self.get_length);
        extend_func(cfg, GET_ITEMS, self.get_items);
        extend_func(cfg, INTO_ITEMS, self.into_items);
        extend_func(cfg, GET_KEYS, self.get_keys);
        extend_func(cfg, INTO_KEYS, self.into_keys);
        extend_func(cfg, GET_VALUES, self.get_values);
        extend_func(cfg, INTO_VALUES, self.into_values);
        extend_func(cfg, CONTAIN, self.contain);
        extend_func(cfg, CONTAIN_ALL, self.contain_all);
        extend_func(cfg, CONTAIN_ANY, self.contain_any);
        extend_func(cfg, SET, self.set);
        extend_func(cfg, SET_MANY, self.set_many);
        extend_func(cfg, GET, self.get);
        extend_func(cfg, GET_MANY, self.get_many);
        extend_func(cfg, REMOVE, self.remove);
        extend_func(cfg, REMOVE_MANY, self.remove_many);
        extend_func(cfg, MOVE, self.move_);
        extend_func(cfg, CLEAR, self.clear);
    }
}

pub fn make(cfg: &mut Cfg, input: Val) -> Val {
    let Val::List(list) = input else {
        return bug!(cfg, "{MAKE}: expected input to be a list, but got {input}");
    };
    let list = List::from(list);
    let mut map: Map<Key, Val> = Map::with_capacity(list.len());
    for item in list {
        let Val::Pair(pair) = item else {
            return bug!(cfg, "{MAKE}: expected input.item to be a pair, but got {item}");
        };
        let pair = Pair::from(pair);
        let Val::Key(key) = pair.left else {
            return bug!(cfg, "{MAKE}: expected input.item.left to be a key, \
                but got {}", pair.left);
        };
        map.insert(key, pair.right);
    }
    Val::Map(map.into())
}

pub fn make_set(cfg: &mut Cfg, input: Val) -> Val {
    let Val::List(list) = input else {
        return bug!(cfg, "{MAKE_SET}: expected input to be a list, but got {input}");
    };
    let list = List::from(list);
    let mut map: Map<Key, Val> = Map::with_capacity(list.len());
    for item in list {
        let Val::Key(key) = item else {
            return bug!(cfg, "{MAKE_SET}: expected input.item to be a key, but got {item}");
        };
        map.insert(key, Val::default());
    }
    Val::Map(map.into())
}

pub fn get_length(cfg: &mut Cfg, ctx: &Val) -> Val {
    let Val::Map(map) = ctx else {
        return bug!(cfg, "{GET_LENGTH}: expected context to be a map, but got {ctx}");
    };
    let len: Int = map.len().into();
    Val::Int(len.into())
}

pub fn get_items(cfg: &mut Cfg, ctx: &Val) -> Val {
    let Val::Map(map) = ctx else {
        return bug!(cfg, "{GET_ITEMS}: expected context to be a map, but got {ctx}");
    };
    let items: List<Val> = map
        .iter()
        .map(|(k, v)| Val::Pair(Pair::new(Val::Key(k.clone()), v.clone()).into()))
        .collect();
    Val::List(items.into())
}

pub fn into_items(cfg: &mut Cfg, ctx: &mut Val) -> Val {
    let Val::Map(map) = ctx else {
        return bug!(cfg, "{INTO_ITEMS}: expected context to be a map, but got {ctx}");
    };
    let mut origin = Map::default();
    swap(&mut **map, &mut origin);
    let items: List<Val> =
        origin.into_iter().map(|(k, v)| Val::Pair(Pair::new(Val::Key(k), v).into())).collect();
    Val::List(items.into())
}

pub fn get_keys(cfg: &mut Cfg, ctx: &Val) -> Val {
    let Val::Map(map) = ctx else {
        return bug!(cfg, "{GET_KEYS}: expected context to be a map, but got {ctx}");
    };
    let keys: List<Val> = map.keys().map(|k| Val::Key(k.clone())).collect();
    Val::List(keys.into())
}

pub fn into_keys(cfg: &mut Cfg, ctx: &mut Val) -> Val {
    let Val::Map(map) = ctx else {
        return bug!(cfg, "{INTO_KEYS}: expected context to be a map, but got {ctx}");
    };
    let mut origin = Map::default();
    swap(&mut **map, &mut origin);
    let keys: List<Val> = origin.into_keys().map(Val::Key).collect();
    Val::List(keys.into())
}

pub fn get_values(cfg: &mut Cfg, ctx: &Val) -> Val {
    let Val::Map(map) = ctx else {
        return bug!(cfg, "{GET_VALUES}: expected context to be a map, but got {ctx}");
    };
    let values: List<Val> = map.values().cloned().collect();
    Val::List(values.into())
}

pub fn into_values(cfg: &mut Cfg, ctx: &mut Val) -> Val {
    let Val::Map(map) = ctx else {
        return bug!(cfg, "{INTO_VALUES}: expected context to be a map, but got {ctx}");
    };
    let mut origin = Map::default();
    swap(&mut **map, &mut origin);
    let values: List<Val> = origin.into_values().collect();
    Val::List(values.into())
}

pub fn contain(cfg: &mut Cfg, ctx: &Val, input: Val) -> Val {
    let Val::Map(map) = ctx else {
        return bug!(cfg, "{CONTAIN}: expected context to be a map, but got {ctx}");
    };
    let Val::Key(key) = input else {
        return bug!(cfg, "{CONTAIN}: expected input to be a key, but got {input}");
    };
    Val::Bit(Bit::from(map.contains_key(&key)))
}

pub fn contain_all(cfg: &mut Cfg, ctx: &Val, input: Val) -> Val {
    let Val::Map(map) = ctx else {
        return bug!(cfg, "{CONTAIN_ALL}: expected context to be a map, but got {ctx}");
    };
    let Val::List(keys) = input else {
        return bug!(cfg, "{CONTAIN_ALL}: expected input to be a list, but got {input}");
    };
    let keys = List::from(keys);
    for key in keys {
        let Val::Key(key) = key else {
            return bug!(cfg, "{CONTAIN_ALL}: expected input.item to be a key, but got {key}");
        };
        if !map.contains_key(&key) {
            return Val::Bit(Bit::from(false));
        }
    }
    Val::Bit(Bit::from(true))
}

pub fn contain_any(cfg: &mut Cfg, ctx: &Val, input: Val) -> Val {
    let Val::Map(map) = ctx else {
        return bug!(cfg, "{CONTAIN_ANY}: expected context to be a map, but got {ctx}");
    };
    let Val::List(keys) = input else {
        return bug!(cfg, "{CONTAIN_ANY}: expected input to be a list, but got {input}");
    };
    let keys = List::from(keys);
    for key in keys {
        let Val::Key(key) = key else {
            return bug!(cfg, "{CONTAIN_ANY}: expected input.item to be a key, but got {key}");
        };
        if map.contains_key(&key) {
            return Val::Bit(Bit::from(true));
        }
    }
    Val::Bit(Bit::from(false))
}

pub fn set(cfg: &mut Cfg, ctx: &mut Val, input: Val) -> Val {
    let Val::Map(map) = ctx else {
        return bug!(cfg, "{SET}: expected context to be a map, but got {ctx}");
    };
    let Val::Pair(key_value) = input else {
        return bug!(cfg, "{SET}: expected input to be a pair, but got {input}");
    };
    let key_value = Pair::from(key_value);
    let Val::Key(key) = key_value.left else {
        return bug!(cfg, "{SET}: expected input.left to be a key, but got {}", key_value.left);
    };
    let value = key_value.right;
    let Some(value) = map.insert(key, value) else {
        return Val::default();
    };
    Val::Cell(Cell::new(value).into())
}

pub fn set_many(cfg: &mut Cfg, ctx: &mut Val, input: Val) -> Val {
    let Val::Map(map) = ctx else {
        return bug!(cfg, "{SET_MANY}: expected context to be a map, but got {ctx}");
    };
    let Val::Map(update) = input else {
        return bug!(cfg, "{SET_MANY}: expected input to be a map, but got {input}");
    };
    let update = Map::from(update);
    let map: Map<Key, Val> =
        update.into_iter().filter_map(|(k, v)| map.insert(k.clone(), v).map(|v| (k, v))).collect();
    Val::Map(map.into())
}

pub fn get(cfg: &mut Cfg, ctx: &Val, input: Val) -> Val {
    let Val::Map(map) = ctx else {
        return bug!(cfg, "{GET}: expected context to be a map, but got {ctx}");
    };
    let Val::Key(key) = input else {
        return bug!(cfg, "{GET}: expected input to be a key, but got {input}");
    };
    let Some(value) = map.get(&key) else {
        return Val::default();
    };
    Val::Cell(Cell::new(value.clone()).into())
}

pub fn get_many(cfg: &mut Cfg, ctx: &Val, input: Val) -> Val {
    let Val::Map(map) = ctx else {
        return bug!(cfg, "{GET_MANY}: expected context to be a map, but got {ctx}");
    };
    let Val::List(keys) = input else {
        return bug!(cfg, "{GET_MANY}: expected input to be a list, but got {input}");
    };
    let keys = List::from(keys);
    let mut new_map: Map<Key, Val> = Map::with_capacity(keys.len());
    for key in keys {
        let Val::Key(key) = key else {
            return bug!(cfg, "{GET_MANY}: expected input.item to be a key, but got {key}");
        };
        if let Some(val) = map.get(&key) {
            new_map.insert(key, val.clone());
        }
    }
    Val::Map(new_map.into())
}

pub fn remove(cfg: &mut Cfg, ctx: &mut Val, input: Val) -> Val {
    let Val::Map(map) = ctx else {
        return bug!(cfg, "{REMOVE}: expected context to be a map, but got {ctx}");
    };
    let Val::Key(key) = input else {
        return bug!(cfg, "{REMOVE}: expected input to be a key, but got {input}");
    };
    let Some(value) = map.remove(&key) else {
        return Val::default();
    };
    Val::Cell(Cell::new(value).into())
}

pub fn remove_many(cfg: &mut Cfg, ctx: &mut Val, input: Val) -> Val {
    let Val::Map(map) = ctx else {
        return bug!(cfg, "{REMOVE_MANY}: expected context to be a map, but got {ctx}");
    };
    let Val::List(keys) = input else {
        return bug!(cfg, "{REMOVE_MANY}: expected input to be a list, but got {input}");
    };
    let keys = List::from(keys);
    let mut new_map: Map<Key, Val> = Map::with_capacity(keys.len());
    for key in keys {
        let Val::Key(key) = key else {
            return bug!(cfg, "{REMOVE_MANY}: expected input.item to be a key, but got {key}");
        };
        if let Some(val) = map.remove(&key) {
            new_map.insert(key, val);
        }
    }
    Val::Map(new_map.into())
}

pub fn move_(cfg: &mut Cfg, ctx: &mut Val, input: Val) -> Val {
    let Val::Map(map) = ctx else {
        return bug!(cfg, "{MOVE}: expected context to be a map, but got {ctx}");
    };
    let Val::Key(key) = input else {
        return bug!(cfg, "{MOVE}: expected input to be a key, but got {input}");
    };
    let Some(value) = map.remove(&key) else {
        return bug!(cfg, "{MOVE}: value not found for key {key} in the map {map}");
    };
    value
}

pub fn clear(cfg: &mut Cfg, ctx: &mut Val) -> Val {
    let Val::Map(map) = ctx else {
        return bug!(cfg, "{CLEAR}: expected context to be a map, but got {ctx}");
    };
    map.clear();
    Val::default()
}