logo-interp 0.1.0

Implements parsing and interpreting of Logo programming language
Documentation
use rand::{Rng, thread_rng};
use crate::core::*;
use crate::executor::execute;
use crate::executor_state::*;

pub fn add_stdlib<S: 'static>(es: &mut EState<S>) {
    es.functions.insert("repeat".to_string(), Function::from_proc2(repeat));
    es.functions.insert("show".to_string(), Function::from_proc1(show));

    es.functions.insert("abs".to_string(), Function::from_fn1(abs));
    es.functions.insert("arctan".to_string(), Function::from_fn1(arctan));
    es.functions.insert("cos".to_string(), Function::from_fn1(cos));
    es.functions.insert("difference".to_string(), Function::from_fn2(difference));
    es.functions.insert("exp".to_string(), Function::from_fn1(exp));
    es.functions.insert("greater?".to_string(), Function::from_fn2(greater));
    es.functions.insert("less?".to_string(), Function::from_fn2(less));
    es.functions.insert("int".to_string(), Function::from_fn1(int));
    es.functions.insert("log".to_string(), Function::from_fn1(log));
    es.functions.insert("ln".to_string(), Function::from_fn1(ln));
    es.functions.insert("minus".to_string(), Function::from_fn1(minus));
    es.functions.insert("pi".to_string(), Function::from_fn(pi));
    es.functions.insert("power".to_string(), Function::from_fn2(power));
    es.functions.insert("product".to_string(), Function::from_fn2(product));
    es.functions.insert("quotient".to_string(), Function::from_fn2(quotient));
    es.functions.insert("remainder".to_string(), Function::from_fn2(remainder));
    es.functions.insert("random".to_string(), Function::from_fn1(random));
    es.functions.insert("round".to_string(), Function::from_fn1(round));
    es.functions.insert("sin".to_string(), Function::from_fn1(sin));
    es.functions.insert("sqrt".to_string(), Function::from_fn1(sqrt));
    es.functions.insert("sum".to_string(), Function::from_fn2(sum));
    es.functions.insert("tan".to_string(), Function::from_fn1(tan));

    es.functions.insert("bf".to_string(), Function::from_fn1(bf));
    es.functions.insert("butfirst".to_string(), Function::from_fn1(bf));
    es.functions.insert("bl".to_string(), Function::from_fn1(bl));
    es.functions.insert("butlast".to_string(), Function::from_fn1(bl));
    es.functions.insert("count".to_string(), Function::from_fn1(count));
    es.functions.insert("empty?".to_string(), Function::from_fn1(empty));
    es.functions.insert("equal?".to_string(), Function::from_fn2(equal));
    es.functions.insert("identical?".to_string(), Function::from_fn2(equal));
    es.functions.insert("first".to_string(), Function::from_fn1(first));
    es.functions.insert("fput".to_string(), Function::from_fn2(fput));
    es.functions.insert("item".to_string(), Function::from_fn2(item));
    es.functions.insert("last".to_string(), Function::from_fn1(last));
    es.functions.insert("list".to_string(), Function::from_fn2(list));
    es.functions.insert("list?".to_string(), Function::from_fn1(is_list));
    es.functions.insert("lput".to_string(), Function::from_fn2(lput));
    es.functions.insert("member?".to_string(), Function::from_fn2(member));
    es.functions.insert("number?".to_string(), Function::from_fn1(number));
    es.functions.insert("pick".to_string(), Function::from_fn1(pick));
    es.functions.insert("word?".to_string(), Function::from_fn1(word));

    es.functions.insert("and".to_string(), Function::from_fn2(and));
    es.functions.insert("or".to_string(), Function::from_fn2(or));
    es.functions.insert("not".to_string(), Function::from_fn1(not));
    es.functions.insert("if".to_string(), Function::from_proc2(if_fn));
    es.functions.insert("ifelse".to_string(), Function::from_proc3(if_else_fn));

    es.functions.insert("make".to_string(), Function::from_proc2(make));
    es.functions.insert("clearname".to_string(), Function::from_proc1(clearname));
    es.functions.insert("clearnames".to_string(), Function::from_proc(clearnames));
    es.functions.insert("name?".to_string(), Function::from_fn1(name));
    es.functions.insert("names".to_string(), Function::from_fn(names));
    es.functions.insert("thing".to_string(), Function::from_fn1(thing));

    es.functions.insert("output".to_string(), Function::from_proc1(output));
}

fn repeat<S>(state: &mut EState<S>, n: i32, cmd: Vec<LogoValue>) -> Result<(), String> {
    for _ in 0..n {
        execute(state, cmd.clone())?;
    }
    Ok(())
}

fn show<S>(_: &mut EState<S>, val: LogoValue) -> Result<(), String> {
    println!("{}", val);
    Ok(())
}

fn abs<S>(_: &mut EState<S>, val: f64) -> Result<f64, String> {
    Ok(val.abs())
}

fn arctan<S>(_: &mut EState<S>, val: f64) -> Result<f64, String> {
    Ok(val.atan().to_degrees())
}

fn cos<S>(_: &mut EState<S>, val: f64) -> Result<f64, String> {
    Ok(val.to_radians().cos())
}

fn difference<S>(_: &mut EState<S>, a: f64, b: f64) -> Result<f64, String> {
    Ok(a - b)
}

fn exp<S>(_: &mut EState<S>, val: f64) -> Result<f64, String> {
    Ok(val.exp())
}

fn greater<S>(_: &mut EState<S>, a: f64, b: f64) -> Result<bool, String> {
    Ok(a > b)
}

fn less<S>(_: &mut EState<S>, a: f64, b: f64) -> Result<bool, String> {
    Ok(a < b)
}

fn int<S>(_: &mut EState<S>, val: f64) -> Result<i32, String> {
    Ok(val as i32)
}

fn log<S>(_: &mut EState<S>, val: f64) -> Result<f64, String> {
    Ok(val.log(10f64))
}

fn ln<S>(_: &mut EState<S>, val: f64) -> Result<f64, String> {
    Ok(val.ln())
}

fn minus<S>(_: &mut EState<S>, val: f64) -> Result<f64, String> {
    Ok(-val)
}

fn pi<S>(_: &mut EState<S>) -> Result<f64, String> {
    Ok(std::f64::consts::PI)
}

fn power<S>(_: &mut EState<S>, a: f64, b: f64) -> Result<f64, String> {
    Ok(a.powf(b))
}

fn product<S>(_: &mut EState<S>, a: f64, b: f64) -> Result<f64, String> {
    Ok(a * b)
}

fn quotient<S>(_: &mut EState<S>, a: f64, b: f64) -> Result<f64, String> {
    Ok(a / b)
}

fn remainder<S>(_: &mut EState<S>, a: i32, b: i32) -> Result<i32, String> {
    Ok(a % b)
}

fn random<S>(_: &mut EState<S>, val: i32) -> Result<i32, String> {
    if val < 1 {
        return Err("Input to random must be greater than 0".to_string());
    }
    Ok((rand::thread_rng().gen::<u32>() % val as u32) as i32)
}

fn round<S>(_: &mut EState<S>, val: f64) -> Result<i32, String> {
    Ok(val.round() as i32)
}

fn sin<S>(_: &mut EState<S>, val: f64) -> Result<f64, String> {
    Ok(val.to_radians().sin())
}

fn sqrt<S>(_: &mut EState<S>, val: f64) -> Result<f64, String> {
    Ok(val.sqrt())
}

fn sum<S>(_: &mut EState<S>, x: f64, y: f64) -> Result<f64, String> {
    Ok(x + y)
}

fn tan<S>(_: &mut EState<S>, val: f64) -> Result<f64, String> {
    Ok(val.to_radians().tan())
}


fn bf<S>(_: &mut EState<S>, mut val: Vec<LogoValue>) -> Result<Vec<LogoValue>, String> {
    if val.len() == 0 {
        return Err("Can't remove an element from an empty list".to_string());
    }
    val.remove(0);
    Ok(val)
}

fn bl<S>(_: &mut EState<S>, mut val: Vec<LogoValue>) -> Result<Vec<LogoValue>, String> {
    if val.len() == 0 {
        return Err("Can't remove an element from an empty list".to_string());
    }
    val.pop();
    Ok(val)
}

fn count<S>(_: &mut EState<S>, val: Vec<LogoValue>) -> Result<i32, String> {
    Ok(val.len() as i32)
}

fn empty<S>(_: &mut EState<S>, val: Vec<LogoValue>) -> Result<bool, String> {
    Ok(val.is_empty())
}

fn equal<S>(_: &mut EState<S>, a: LogoValue, b: LogoValue) -> Result<bool, String> {
    Ok(a == b)
}

fn first<S>(_: &mut EState<S>, val: Vec<LogoValue>) -> Result<LogoValue, String> {
    if val.len() == 0 {
        return Err("Can't get an element from an empty list".to_string());
    }
    Ok(val.first().unwrap().clone())
}

fn fput<S>(_: &mut EState<S>, a: LogoValue, mut b: Vec<LogoValue>) -> Result<Vec<LogoValue>, String> {
    b.insert(0, a);
    Ok(b)
}

fn item<S>(_: &mut EState<S>, idx: i32, val: Vec<LogoValue>) -> Result<LogoValue, String> {
    if idx < 0 || idx >= val.len() as i32 {
        return Err("No such item".to_string());
    }
    Ok(val[idx as usize].clone())
}

fn last<S>(_: &mut EState<S>, val: Vec<LogoValue>) -> Result<LogoValue, String> {
    if val.len() == 0 {
        return Err("Can't get an element from an empty list".to_string());
    }
    Ok(val.last().unwrap().clone())
}

fn list<S>(_: &mut EState<S>, mut a: Vec<LogoValue>, mut b: Vec<LogoValue>) -> Result<Vec<LogoValue>, String> {
    a.append(&mut b);
    Ok(a)
}

fn is_list<S>(_: &mut EState<S>, a: LogoValue) -> Result<bool, String> {
    if let LogoValue::List(_) = a {
        Ok(true)
    }
    else {
        Ok(false)
    }
}

fn lput<S>(_: &mut EState<S>, a: LogoValue, mut b: Vec<LogoValue>) -> Result<Vec<LogoValue>, String> {
    b.push(a);
    Ok(b)
}

fn member<S>(_: &mut EState<S>, a: LogoValue, b: Vec<LogoValue>) -> Result<bool, String> {
    for b_el in b {
        if a == b_el {
            return Ok(true)
        }
    }
    Ok(false)
}

fn number<S>(_: &mut EState<S>, a: LogoValue) -> Result<bool, String> {
    if let LogoValue::Word(word) = a {
        if let Ok(_) = word.0.parse::<f32>() {
            Ok(true)
        }
        else {
            Ok(false)
        }
    }
    else {
        Ok(false)
    }
}

fn pick<S>(_: &mut EState<S>, val: Vec<LogoValue>) -> Result<LogoValue, String> {
    if val.len() == 0 {
        return Err("Can't get an element from an empty list".to_string());
    }
    Ok(val[thread_rng().gen::<usize>() % val.len()].clone())
}

fn word<S>(_: &mut EState<S>, a: LogoValue) -> Result<bool, String> {
    if let LogoValue::Word(_) = a {
        Ok(true)
    }
    else {
        Ok(false)
    }
}


fn and<S>(_: &mut EState<S>, a: bool, b: bool) -> Result<bool, String> {
    Ok(a && b)
}

fn or<S>(_: &mut EState<S>, a: bool, b: bool) -> Result<bool, String> {
    Ok(a || b)
}

fn not<S>(_: &mut EState<S>, a: bool) -> Result<bool, String> {
    Ok(!a)
}

fn if_fn<S>(state: &mut EState<S>, a: bool, cmd: Vec<LogoValue>) -> Result<(), String> {
    if a {
        execute(state, cmd)?;
    }
    Ok(())
}

fn if_else_fn<S>(state: &mut EState<S>, a: bool, cmd_true: Vec<LogoValue>, cmd_false: Vec<LogoValue>) -> Result<(), String> {
    if a {
        execute(state, cmd_true)?;
    }
    else {
        execute(state, cmd_false)?;
    }
    Ok(())
}


fn make<S>(state: &mut EState<S>, name: String, val: LogoValue) -> Result<(), String> {
    state.vars.insert(name.to_lowercase(), val);
    Ok(())
}

fn clearname<S>(state: &mut EState<S>, name: String) -> Result<(), String> {
    state.vars.remove(name.to_lowercase().as_str());
    Ok(())
}

fn clearnames<S>(state: &mut EState<S>) -> Result<(), String> {
    state.vars.clear();
    Ok(())
}

fn name<S>(state: &mut EState<S>, name: String) -> Result<bool, String> {
    Ok(state.vars.contains_key(name.to_lowercase().as_str()))
}

fn names<S>(state: &mut EState<S>) -> Result<Vec<LogoValue>, String> {
    let mut result = Vec::with_capacity(state.vars.len());
    for key in state.vars.keys() {
        result.push(LogoValue::String(key.clone()));
    }
    Ok(result)
}

fn thing<S>(state: &mut EState<S>, name: String) -> Result<LogoValue, String> {
    let name = name.to_lowercase();
    if !state.vars.contains_key(name.as_str()) {
        return Err("No such variable".to_string());
    }
    Ok(state.vars[name.as_str()].clone())
}


fn output<S>(state: &mut EState<S>, val: LogoValue) -> Result<(), String> {
    state.output = Some(val);
    Err("Output".to_string())
}