cljrs-eval 0.1.7

Tree-walking interpreter and macro expander for clojurust
Documentation
use cljrs_gc::GcPtr;
use cljrs_value::regex::{MatchPhase, Matcher};
use cljrs_value::{Value, ValueError, ValueResult};

pub fn builtin_re_pattern(args: &[Value]) -> ValueResult<Value> {
    match &args[0] {
        Value::Pattern(pattern) => Ok(Value::Pattern(pattern.clone())),
        Value::Matcher(matcher) => Ok(Value::Pattern(matcher.get().pattern.clone())),
        Value::Str(s) => {
            let pattern = regex::Regex::new(s.get().as_str());
            match pattern {
                Ok(pattern) => Ok(Value::Pattern(GcPtr::new(pattern))),
                Err(e) => Err(ValueError::Other(e.to_string())),
            }
        }
        v => Err(ValueError::WrongType {
            expected: "str",
            got: v.type_name().to_string(),
        }),
    }
}

fn get_match(matcher: &Matcher) -> ValueResult<Value> {
    let state = matcher.next();
    match state {
        MatchPhase::Matching(_) => match matcher.capture() {
            Some(c) => Ok(c.to_value()),
            None => Ok(Value::Nil),
        },
        _ => Ok(Value::Nil),
    }
}

pub fn builtin_re_find(args: &[Value]) -> ValueResult<Value> {
    if args.len() == 1 {
        match &args[0] {
            Value::Matcher(m) => get_match(m.get()),
            v => Err(ValueError::WrongType {
                expected: "matcher",
                got: v.type_name().to_string(),
            }),
        }
    } else if args.len() == 2 {
        let matcher = new_matcher(args, false)?;
        get_match(&matcher)
    } else {
        Err(ValueError::ArityError {
            name: "re-find".to_string(),
            expected: "1 or 2".to_string(),
            got: args.len(),
        })
    }
}

pub fn builtin_re_matches(args: &[Value]) -> ValueResult<Value> {
    let matcher = new_matcher(args, true)?;
    matcher.next();
    Ok(match matcher.capture() {
        Some(c) => c.to_value(),
        None => Value::Nil,
    })
}

pub fn builtin_re_groups(args: &[Value]) -> ValueResult<Value> {
    match &args[0] {
        Value::Matcher(m) => match m.get().capture() {
            Some(c) => Ok(c.to_value()),
            None => Ok(Value::Nil),
        },
        v => Err(ValueError::WrongType {
            expected: "matcher",
            got: v.type_name().to_string(),
        }),
    }
}

fn new_matcher(args: &[Value], match_all: bool) -> ValueResult<Matcher> {
    let pattern = match &args[0] {
        Value::Pattern(p) => Ok(p.get().clone()),
        Value::Str(s) => regex::Regex::new(s.get()),
        v => {
            return Err(ValueError::WrongType {
                expected: "str or pattern",
                got: v.type_name().to_string(),
            });
        }
    };
    let haystack = match &args[1] {
        Value::Str(s) => s.get().to_string(),
        v => {
            return Err(ValueError::WrongType {
                expected: "str",
                got: v.type_name().to_string(),
            });
        }
    };
    match pattern {
        Ok(pattern) => Ok(Matcher::new(pattern, haystack, match_all)),
        Err(e) => Err(ValueError::Other(e.to_string())),
    }
}

pub(crate) fn builtin_re_matcher(args: &[Value]) -> ValueResult<Value> {
    new_matcher(args, false).map(|m| Value::Matcher(GcPtr::new(m)))
}