harn-vm 0.5.54

Async bytecode virtual machine for the Harn programming language
Documentation
use std::rc::Rc;

use crate::value::{values_equal, VmError, VmValue};
use crate::vm::Vm;

pub(crate) fn register_set_builtins(vm: &mut Vm) {
    vm.register_builtin("set", |args, _out| {
        let mut items: Vec<VmValue> = Vec::new();
        for arg in args {
            match arg {
                VmValue::List(list) => {
                    for v in list.iter() {
                        if !items.iter().any(|x| values_equal(x, v)) {
                            items.push(v.clone());
                        }
                    }
                }
                VmValue::Set(s) => {
                    for v in s.iter() {
                        if !items.iter().any(|x| values_equal(x, v)) {
                            items.push(v.clone());
                        }
                    }
                }
                other => {
                    if !items.iter().any(|x| values_equal(x, other)) {
                        items.push(other.clone());
                    }
                }
            }
        }
        Ok(VmValue::Set(Rc::new(items)))
    });

    vm.register_builtin("set_add", |args, _out| {
        let s = match args.first() {
            Some(VmValue::Set(s)) => s.clone(),
            _ => {
                return Err(VmError::Thrown(VmValue::String(Rc::from(
                    "set_add: first argument must be a set",
                ))));
            }
        };
        let val = args.get(1).cloned().unwrap_or(VmValue::Nil);
        let mut items: Vec<VmValue> = (*s).clone();
        if !items.iter().any(|x| values_equal(x, &val)) {
            items.push(val);
        }
        Ok(VmValue::Set(Rc::new(items)))
    });

    vm.register_builtin("set_remove", |args, _out| {
        let s = match args.first() {
            Some(VmValue::Set(s)) => s.clone(),
            _ => {
                return Err(VmError::Thrown(VmValue::String(Rc::from(
                    "set_remove: first argument must be a set",
                ))));
            }
        };
        let val = args.get(1).cloned().unwrap_or(VmValue::Nil);
        let items: Vec<VmValue> = s
            .iter()
            .filter(|x| !values_equal(x, &val))
            .cloned()
            .collect();
        Ok(VmValue::Set(Rc::new(items)))
    });

    vm.register_builtin("set_contains", |args, _out| {
        let s = match args.first() {
            Some(VmValue::Set(s)) => s,
            _ => return Ok(VmValue::Bool(false)),
        };
        let val = args.get(1).unwrap_or(&VmValue::Nil);
        Ok(VmValue::Bool(s.iter().any(|x| values_equal(x, val))))
    });

    vm.register_builtin("set_union", |args, _out| {
        let a = match args.first() {
            Some(VmValue::Set(s)) => s,
            _ => {
                return Err(VmError::Thrown(VmValue::String(Rc::from(
                    "set_union: arguments must be sets",
                ))));
            }
        };
        let b = match args.get(1) {
            Some(VmValue::Set(s)) => s,
            _ => {
                return Err(VmError::Thrown(VmValue::String(Rc::from(
                    "set_union: arguments must be sets",
                ))));
            }
        };
        let mut items: Vec<VmValue> = (**a).clone();
        for v in b.iter() {
            if !items.iter().any(|x| values_equal(x, v)) {
                items.push(v.clone());
            }
        }
        Ok(VmValue::Set(Rc::new(items)))
    });

    vm.register_builtin("set_intersect", |args, _out| {
        let a = match args.first() {
            Some(VmValue::Set(s)) => s,
            _ => {
                return Err(VmError::Thrown(VmValue::String(Rc::from(
                    "set_intersect: arguments must be sets",
                ))));
            }
        };
        let b = match args.get(1) {
            Some(VmValue::Set(s)) => s,
            _ => {
                return Err(VmError::Thrown(VmValue::String(Rc::from(
                    "set_intersect: arguments must be sets",
                ))));
            }
        };
        let items: Vec<VmValue> = a
            .iter()
            .filter(|x| b.iter().any(|y| values_equal(x, y)))
            .cloned()
            .collect();
        Ok(VmValue::Set(Rc::new(items)))
    });

    vm.register_builtin("set_difference", |args, _out| {
        let a = match args.first() {
            Some(VmValue::Set(s)) => s,
            _ => {
                return Err(VmError::Thrown(VmValue::String(Rc::from(
                    "set_difference: arguments must be sets",
                ))));
            }
        };
        let b = match args.get(1) {
            Some(VmValue::Set(s)) => s,
            _ => {
                return Err(VmError::Thrown(VmValue::String(Rc::from(
                    "set_difference: arguments must be sets",
                ))));
            }
        };
        let items: Vec<VmValue> = a
            .iter()
            .filter(|x| !b.iter().any(|y| values_equal(x, y)))
            .cloned()
            .collect();
        Ok(VmValue::Set(Rc::new(items)))
    });

    vm.register_builtin("set_symmetric_difference", |args, _out| {
        let a = match args.first() {
            Some(VmValue::Set(s)) => s,
            _ => {
                return Err(VmError::Thrown(VmValue::String(Rc::from(
                    "set_symmetric_difference: arguments must be sets",
                ))));
            }
        };
        let b = match args.get(1) {
            Some(VmValue::Set(s)) => s,
            _ => {
                return Err(VmError::Thrown(VmValue::String(Rc::from(
                    "set_symmetric_difference: arguments must be sets",
                ))));
            }
        };
        let mut items: Vec<VmValue> = a
            .iter()
            .filter(|x| !b.iter().any(|y| values_equal(x, y)))
            .cloned()
            .collect();
        for v in b.iter() {
            if !a.iter().any(|x| values_equal(x, v)) {
                items.push(v.clone());
            }
        }
        Ok(VmValue::Set(Rc::new(items)))
    });

    vm.register_builtin("set_is_subset", |args, _out| {
        let a = match args.first() {
            Some(VmValue::Set(s)) => s,
            _ => return Ok(VmValue::Bool(false)),
        };
        let b = match args.get(1) {
            Some(VmValue::Set(s)) => s,
            _ => return Ok(VmValue::Bool(false)),
        };
        Ok(VmValue::Bool(
            a.iter().all(|x| b.iter().any(|y| values_equal(x, y))),
        ))
    });

    vm.register_builtin("set_is_superset", |args, _out| {
        let a = match args.first() {
            Some(VmValue::Set(s)) => s,
            _ => return Ok(VmValue::Bool(false)),
        };
        let b = match args.get(1) {
            Some(VmValue::Set(s)) => s,
            _ => return Ok(VmValue::Bool(false)),
        };
        Ok(VmValue::Bool(
            b.iter().all(|x| a.iter().any(|y| values_equal(x, y))),
        ))
    });

    vm.register_builtin("set_is_disjoint", |args, _out| {
        let a = match args.first() {
            Some(VmValue::Set(s)) => s,
            _ => return Ok(VmValue::Bool(true)),
        };
        let b = match args.get(1) {
            Some(VmValue::Set(s)) => s,
            _ => return Ok(VmValue::Bool(true)),
        };
        Ok(VmValue::Bool(
            !a.iter().any(|x| b.iter().any(|y| values_equal(x, y))),
        ))
    });
}