oftlisp 0.1.3

A compiler and interpreter for OftLisp, in Rust.
Documentation
//! Utility functions.

use std::collections::HashMap;

use gc::{Gc, Trace};

use context::Context;
use symbol::Symbol;
use value::Value;

/// A version of `void::Void` that is GC-able.
#[derive(Clone, Debug, Finalize)]
pub enum GcVoid {}

unsafe impl Trace for GcVoid {
    unsafe_empty_trace!();
}

/// Converts a `Value` to a `Vec<Value>`. Will only convert cons-lists, not
/// vectors.
pub fn as_list<C: 'static + Context>(val: Gc<Value<C>>) -> Option<Vec<Gc<Value<C>>>> {
    let mut out = Vec::new();
    let mut val: &Value<C> = &*val;
    while let &Value::Cons(ref h, ref t, _) = val {
        out.push(h.clone());
        val = t;
    }
    if let &Value::Nil(_) = val {
        Some(out)
    } else {
        None
    }
}

/// Converts a `Value` to a `Vec<(Value, ValueMeta)>`. Will only convert
/// cons-lists, not vectors.
pub fn as_list_meta<C: 'static + Context>(
    val: Gc<Value<C>>,
) -> Option<Vec<(Gc<Value<C>>, C::ValueMeta)>> {
    let mut out = Vec::new();
    let mut val: &Value<C> = &*val;
    while let &Value::Cons(ref h, ref t, ref m) = val {
        out.push((h.clone(), m.clone()));
        val = t;
    }
    if let &Value::Nil(_) = val {
        Some(out)
    } else {
        None
    }
}

/// Converts the given `Value` to a symbol and rest values.
pub fn as_shl<C: 'static + Context>(val: Gc<Value<C>>) -> Option<(Symbol, Vec<Gc<Value<C>>>)> {
    if let Value::Cons(ref h, ref t, _) = *val {
        if let Value::Symbol(sym, _) = **h {
            as_list(t.clone()).map(|t| (sym, t))
        } else {
            None
        }
    } else {
        None
    }
}

/// Converts a `Value` to a `Vec<Symbol>`. Will only convert cons-lists, not
/// vectors.
pub fn as_sym_list<C: 'static + Context>(val: Gc<Value<C>>) -> Option<Vec<Symbol>> {
    let mut out = Vec::new();
    for val in try_opt!(as_list(val)) {
        if let &Value::Symbol(s, _) = &*val {
            out.push(s);
        } else {
            return None;
        }
    }
    Some(out)
}

/// Converts a list of symbol-value pairs to a hashmap.
pub fn from_assoc<C: 'static + Context>(
    val: Gc<Value<C>>,
) -> Option<HashMap<Symbol, Gc<Value<C>>>> {
    let list = try_opt!(as_list(val));
    let mut map = HashMap::new();
    for val in list {
        match *val {
            Value::Cons(ref h, ref t, _) => {
                match **h {
                    Value::Symbol(s, _) => map.insert(s, t.clone()),
                    _ => return None,
                }
            }
            _ => return None,
        };
    }
    Some(map)
}

/// Converts a `Vec<(Value, ValueMeta)>` to a cons-list.
pub fn from_list_meta<C: 'static + Context>(
    mut vec: Vec<(Gc<Value<C>>, C::ValueMeta)>,
) -> Gc<Value<C>> {
    let mut out = Gc::new(Value::Nil(C::ValueMeta::default()));
    while let Some((val, meta)) = vec.pop() {
        out = Gc::new(Value::Cons(val, out, meta));
    }
    out
}

/// Returns whether the given import path is for a module that is "internal,"
/// that is, a module that will not automatically import `std/prelude`.
pub fn is_internal(import_path: Symbol) -> bool {
    let s = import_path.as_str();
    s == "std/prelude" || s.starts_with("std/internal/prelude/")
}

/// Returns whether the given `Value` is a cons-list whose head is the given
/// symbol.
pub fn is_shl<C: 'static + Context>(sym: Symbol, val: Gc<Value<C>>) -> bool {
    if let Value::Cons(ref h, _, _) = *val {
        if let Value::Symbol(s, _) = **h {
            s == sym
        } else {
            false
        }
    } else {
        false
    }
}

/// Converts a `HashMap<Symbol, Value>` (or a compatible `IntoIterator`) to an
/// assoc.
pub fn to_assoc<'a, C, I>(map: I) -> Gc<Value<C>>
where
    C: 'static + Context,
    I: IntoIterator<Item = (&'a Symbol, &'a Gc<Value<C>>)>,
{
    let mut list = vec![];
    for (&k, v) in map {
        let k = Gc::new(Value::Symbol(k, Default::default()));
        let v = v.clone();
        list.push(Gc::new(Value::Cons(k, v, Default::default())));
    }
    Value::list(list, Default::default())
}