logo-interp 0.1.0

Implements parsing and interpreting of Logo programming language
Documentation
use std::collections::HashMap;
use std::rc::Rc;
use crate::core::*;


#[derive(Clone)]
pub struct Function<S> {
    pub f: Rc<dyn Fn(&mut EState<S>, Vec<LogoValue>) -> Result<Option<LogoValue>, String>>,
    pub args: i32
}

pub struct EState<S> {
    pub functions: HashMap<String, Function<S>>,
    pub logo_procedures: HashMap<String, LogoProcedure>,
    pub vars: HashMap<String, LogoValue>,
    pub output: Option<LogoValue>,
    pub state: S
}

impl<S: 'static> Function<S> {
    pub fn from_proc(f: fn(&mut EState<S>) -> Result<(), String>) -> Self {
        return Function{f: Rc::new(
            move |state: &mut EState<S>, _: Vec<LogoValue>| -> Result<Option<LogoValue>, String> {
                f(state)?;
                return Ok(None);
        }), args: 0};
    }
    pub fn from_fn<Out: LogoConvertible + 'static>(f: fn(&mut EState<S>) -> Result<Out, String>) -> Self {
        return Function{f: Rc::new(move |state: &mut EState<S>, _: Vec<LogoValue>| -> Result<Option<LogoValue>, String> {
            return Ok(Some(f(state)?.to_logo()));
        }), args: 0};
    }

    pub fn from_proc1<T1: LogoConvertible + 'static>
    (f: fn(&mut EState<S>, T1) -> Result<(), String>) -> Self {
        return Function{f: Rc::new(move |state: &mut EState<S>, mut args: Vec<LogoValue>| -> Result<Option<LogoValue>, String> {
            let arg1 = T1::from_logo(args.pop().unwrap())?;
            f(state, arg1)?;
            return Ok(None);
        }), args: 1};
    }
    pub fn from_fn1<T1: LogoConvertible + 'static, Out: LogoConvertible + 'static>
    (f: fn(&mut EState<S>, T1) -> Result<Out, String>) -> Self {
        return Function{f: Rc::new(move |state: &mut EState<S>, mut args: Vec<LogoValue>| -> Result<Option<LogoValue>, String> {
            let arg1 = T1::from_logo(args.pop().unwrap())?;
            return Ok(Some(f(state, arg1)?.to_logo()));
        }), args: 1};
    }

    pub fn from_proc2<T1: LogoConvertible + 'static, T2: LogoConvertible + 'static>
    (f: fn(&mut EState<S>, T1, T2) -> Result<(), String>) -> Self {
        return Function{f: Rc::new(move |state: &mut EState<S>, mut args: Vec<LogoValue>| -> Result<Option<LogoValue>, String> {
            let arg2 = T2::from_logo(args.pop().unwrap())?;
            let arg1 = T1::from_logo(args.pop().unwrap())?;
            f(state, arg1, arg2)?;
            return Ok(None);
        }), args: 2};
    }
    pub fn from_fn2<T1: LogoConvertible + 'static, T2: LogoConvertible + 'static, Out: LogoConvertible + 'static>
    (f: fn(&mut EState<S>, T1, T2) -> Result<Out, String>) -> Self {
        return Function{f: Rc::new(move |state: &mut EState<S>, mut args: Vec<LogoValue>| -> Result<Option<LogoValue>, String> {
            let arg2 = T2::from_logo(args.pop().unwrap())?;
            let arg1 = T1::from_logo(args.pop().unwrap())?;
            return Ok(Some(f(state, arg1, arg2)?.to_logo()));
        }), args: 2};
    }

    pub fn from_proc3<T1: LogoConvertible + 'static, T2: LogoConvertible + 'static, T3: LogoConvertible + 'static>
    (f: fn(&mut EState<S>, T1, T2, T3) -> Result<(), String>) -> Self {
        return Function{f: Rc::new(move |state: &mut EState<S>, mut args: Vec<LogoValue>| -> Result<Option<LogoValue>, String> {
            let arg3 = T3::from_logo(args.pop().unwrap())?;
            let arg2 = T2::from_logo(args.pop().unwrap())?;
            let arg1 = T1::from_logo(args.pop().unwrap())?;
            f(state, arg1, arg2, arg3)?;
            return Ok(None);
        }), args: 3};
    }
    pub fn from_fn3<T1: LogoConvertible + 'static, T2: LogoConvertible + 'static, T3: LogoConvertible + 'static, Out: LogoConvertible + 'static>
    (f: fn(&mut EState<S>, T1, T2, T3) -> Result<Out, String>) -> Self {
        return Function{f: Rc::new(move |state: &mut EState<S>, mut args: Vec<LogoValue>| -> Result<Option<LogoValue>, String> {
            let arg3 = T3::from_logo(args.pop().unwrap())?;
            let arg2 = T2::from_logo(args.pop().unwrap())?;
            let arg1 = T1::from_logo(args.pop().unwrap())?;
            return Ok(Some(f(state, arg1, arg2, arg3)?.to_logo()));
        }), args: 3};
    }
}

impl<S> EState<S> {
    pub fn new(state: S) -> Self {
        return EState {
            functions: HashMap::new(),
            logo_procedures: HashMap::new(),
            vars: HashMap::new(),
            output: None,
            state
        };
    }
}

#[test]
fn test_executor_function() {
    let mut state = EState::new(5);

    let sum = |_: &mut EState<i32>, x: f64, y: f64| -> Result<f64, String> {
        Ok(x + y)
    };
    state.functions.insert("sum".to_string(), Function::from_fn2(sum));

    let sum_fn = state.functions[&"sum".to_string()].clone();
    assert_eq!(sum_fn.args, 2);
    let res = (sum_fn.f)(&mut state,
                         vec![LogoValue::Word(Word("2".to_string())), LogoValue::Word(Word("3".to_string()))]);
    assert!(res.is_ok());
    assert!(res.as_ref().unwrap().is_some());
    assert_eq!(res.unwrap().unwrap(), LogoValue::Word(Word("5".to_string())));
}