sainome 0.1.5

A generic dice bot for RPG
Documentation
use super::RunTime;
use crate::ast::*;
use crate::parser;
use crate::ExecResult;
use crate::Value;
use std::rc::Rc;

pub fn exec<'a>(code: &str, run_time: &RunTime<'a>) -> Option<ExecResult> {
    let ast = parser::parse::expr(code);
    let value = match &ast {
        Ok(expr) => {
            let res = exec_expr(expr, &mut run_time.clone());
            res.map(|x| ExecResult::from(x.as_ref()))
        }
        Err(x) => Some(ExecResult::Err(x.to_string())),
    };
    value
}

fn exec_expr<'a>(expr: &Expr, run_time: &mut RunTime<'a>) -> Option<Rc<Value<'a>>> {
    match expr {
        Expr::Assign(ident, fnc_chain) => {
            let value = exec_branch(fnc_chain, run_time);
            if let Some(value) = value {
                run_time.env.insert(Rc::clone(ident), Rc::clone(&value));
                Some(value)
            } else {
                None
            }
        }
        Expr::Branch(branch) => exec_branch(branch, run_time),
    }
}

fn exec_branch<'a>(branch: &Branch, run_time: &mut RunTime<'a>) -> Option<Rc<Value<'a>>> {
    match branch {
        Branch::Branch(c, left, right) => {
            if let Some(c) = exec_fnc_chain(c, run_time) {
                if let Value::Bool(c) = c.as_ref() {
                    if *c {
                        exec_branch(left, run_time)
                    } else {
                        exec_branch(right, run_time)
                    }
                } else {
                    None
                }
            } else {
                None
            }
        }
        Branch::FncChain(fnc_chain) => exec_fnc_chain(fnc_chain, run_time),
    }
}

fn exec_fnc_chain<'b>(fnc_chain: &FncChain, run_time: &mut RunTime<'b>) -> Option<Rc<Value<'b>>> {
    match fnc_chain {
        FncChain::FncChain(left, right) => {
            let left = exec_fnc_chain(left, run_time);
            let right = exec_fnc_chain(right, run_time);
            if let (Some(left), Some(right)) = (left, right) {
                call_like_fnc_with_value(&right, left, run_time)
            } else {
                None
            }
        }
        FncChain::FncDef(fnc_def) => exec_fnc_def(fnc_def, run_time),
    }
}

fn exec_fnc_def<'b>(fnc_def: &FncDef, run_time: &mut RunTime<'b>) -> Option<Rc<Value<'b>>> {
    match fnc_def {
        FncDef::FncDef(arg, right) => {
            let arg = Rc::clone(arg);
            let right = Rc::clone(right);
            let run_time = run_time.clone();
            Some(Rc::new(Value::Fnc(Box::new(
                move |argv| -> Option<Rc<Value<'b>>> {
                    let mut run_time = run_time.clone();
                    run_time.env.insert(Rc::clone(&arg), argv);
                    exec_fnc_def(&Rc::clone(&right), &mut run_time)
                },
            ))))
        }
        FncDef::Expr0(expr_0) => exec_expr_0(expr_0, run_time),
    }
}

fn exec_expr_0<'b>(expr_0: &Expr0, run_time: &mut RunTime<'b>) -> Option<Rc<Value<'b>>> {
    match expr_0 {
        Expr0::Expr0(left, right, op_code) => {
            let right = exec_expr_0(right, run_time);
            if let Some(right) = right {
                match op_code {
                    OpCode0::At => match right.as_ref() {
                        Value::Fnc(fnc) => {
                            let mut value = vec![];
                            loop {
                                let left = exec_expr_0(left, run_time);
                                let f = left
                                    .and_then(|left| {
                                        value.push(Rc::clone(&left));
                                        fnc(Rc::clone(&left))
                                    })
                                    .and_then(|f| match f.as_ref() {
                                        Value::Bool(f) => Some(*f),
                                        _ => None,
                                    });
                                if let Some(f) = f {
                                    if !f {
                                        break;
                                    }
                                } else {
                                    return None;
                                }
                            }
                            Some(Rc::new(Value::List(Rc::new(value))))
                        }
                        Value::Num(n) => {
                            let mut value = vec![];
                            loop {
                                let left = exec_expr_0(left, run_time);
                                let f = left.and_then(|left| {
                                    value.push(Rc::clone(&left));
                                    match left.as_ref() {
                                        Value::Num(m) => Some(*m >= *n),
                                        _ => None,
                                    }
                                });
                                if let Some(f) = f {
                                    if !f {
                                        break;
                                    }
                                } else {
                                    return None;
                                }
                            }
                            Some(Rc::new(Value::List(Rc::new(value))))
                        }
                        _ => None,
                    },
                }
            } else {
                None
            }
        }
        Expr0::Expr1(expr_1) => exec_expr_1(expr_1, run_time),
    }
}

fn exec_expr_1<'b>(expr_1: &Expr1, run_time: &mut RunTime<'b>) -> Option<Rc<Value<'b>>> {
    match expr_1 {
        Expr1::Expr1(left, right, op_code) => {
            let left = exec_expr_1(left, run_time);
            let right = exec_expr_1(right, run_time);
            if let (Some(left), Some(right)) = (left, right) {
                let (left, right) = (left.as_ref(), right.as_ref());
                if let (Value::Bool(left), Value::Bool(right)) = (left, right) {
                    exec_expr_1_bool(*left, *right, op_code)
                } else if let (Value::Str(left), Value::Str(right)) = (left, right) {
                    exec_expr_1_str(&left, &right, op_code)
                } else if let (Value::Num(left), Value::Num(right)) = (left, right) {
                    exec_expr_1_num(*left, *right, op_code)
                } else {
                    None
                }
            } else {
                None
            }
        }
        Expr1::Expr2(expr_2) => exec_expr_2(expr_2, run_time),
    }
}

fn exec_expr_2<'b>(expr_2: &Expr2, run_time: &mut RunTime<'b>) -> Option<Rc<Value<'b>>> {
    match expr_2 {
        Expr2::Expr2(left, right, op_code) => {
            let left = exec_expr_2(left, run_time);
            let right = exec_expr_2(right, run_time);
            if let (Some(left), Some(right)) = (left, right) {
                let (left, right) = (left.as_ref(), right.as_ref());
                if let (Value::Bool(left), Value::Bool(right)) = (left, right) {
                    exec_expr_2_bool(*left, *right, op_code)
                } else if let (Value::Str(left), Value::Str(right)) = (left, right) {
                    exec_expr_2_str(&left, &right, op_code)
                } else if let (Value::Num(left), Value::Num(right)) = (left, right) {
                    exec_expr_2_num(*left, *right, op_code)
                } else if let (Value::List(left), Value::List(right)) = (left, right) {
                    exec_expr_2_list(&left, &right, op_code)
                } else {
                    None
                }
            } else {
                None
            }
        }
        Expr2::Expr3(expr_3) => exec_expr_3(expr_3, run_time),
    }
}

fn exec_expr_3<'b>(expr_3: &Expr3, run_time: &mut RunTime<'b>) -> Option<Rc<Value<'b>>> {
    match expr_3 {
        Expr3::Expr3(left, right, op_code) => {
            let left = exec_expr_3(left, run_time);
            let right = exec_expr_3(right, run_time);
            if let (Some(left), Some(right)) = (&left, &right) {
                let (left, right) = (left.as_ref(), right.as_ref());
                if let (Value::Bool(left), Value::Bool(right)) = (left, right) {
                    exec_expr_3_bool(*left, *right, op_code)
                } else if let (Value::Num(left), Value::Num(right)) = (left, right) {
                    exec_expr_3_num(*left, *right, op_code)
                } else {
                    None
                }
            } else {
                None
            }
        }
        Expr3::Expr4(expr_4) => exec_expr_4(expr_4, run_time),
    }
}

fn exec_expr_4<'b>(expr_4: &Expr4, run_time: &mut RunTime<'b>) -> Option<Rc<Value<'b>>> {
    match expr_4 {
        Expr4::Expr4(left, right, op_code) => {
            let left = exec_expr_4(left, run_time);
            let right = exec_expr_4(right, run_time);
            if let (Some(left), Some(right)) = (left, right) {
                let (left, right) = (left.as_ref(), right.as_ref());
                if let (Value::Num(left), Value::Num(right)) = (left, right) {
                    exec_expr_4_num(*left, *right, op_code, run_time)
                } else {
                    None
                }
            } else {
                None
            }
        }
        Expr4::Unary(unary) => exec_expr_unary(unary, run_time),
    }
}

fn exec_expr_unary<'b>(unary: &Unary, run_time: &mut RunTime<'b>) -> Option<Rc<Value<'b>>> {
    match unary {
        Unary::Plus(fnc_call) => {
            if let Some(val) = exec_fnc_call(fnc_call, run_time) {
                match val.as_ref() {
                    Value::Bool(val) => exec_expr_2_bool(false, *val, &OpCode2::Add),
                    Value::List(val) => exec_expr_2_list(&vec![], val, &OpCode2::Add),
                    Value::Num(val) => exec_expr_2_num(0.0, *val, &OpCode2::Add),
                    Value::Str(val) => exec_expr_2_str(&String::from(""), val, &OpCode2::Add),
                    _ => None,
                }
            } else {
                None
            }
        }
        Unary::Minus(fnc_call) => {
            if let Some(val) = exec_fnc_call(fnc_call, run_time) {
                match val.as_ref() {
                    Value::Bool(val) => exec_expr_2_bool(false, *val, &OpCode2::Sub),
                    Value::List(val) => exec_expr_2_list(&vec![], val, &OpCode2::Sub),
                    Value::Num(val) => exec_expr_2_num(0.0, *val, &OpCode2::Sub),
                    Value::Str(val) => exec_expr_2_str(&String::from(""), val, &OpCode2::Sub),
                    _ => None,
                }
            } else {
                None
            }
        }
        Unary::FncCall(fnc_call) => exec_fnc_call(fnc_call, run_time),
    }
}

fn exec_fnc_call<'b>(fnc_call: &FncCall, run_time: &mut RunTime<'b>) -> Option<Rc<Value<'b>>> {
    match fnc_call {
        FncCall::FncCall(fnc_call, arg) => {
            let fnc = exec_fnc_call(fnc_call, run_time);
            if let Some(fnc) = fnc {
                let fnc = fnc.as_ref();
                call_like_fnc_with_term(fnc, arg, run_time)
            } else {
                None
            }
        }
        FncCall::Reducer(reducer) => exec_reducer(reducer, run_time),
    }
}

fn exec_reducer<'b>(reducer: &Reducer, run_time: &mut RunTime<'b>) -> Option<Rc<Value<'b>>> {
    match reducer {
        Reducer::RLeft(i, lst) => {
            let i = exec_reducer(i, run_time);
            let lst = exec_reducer(lst, run_time);
            if let (Some(i), Some(lst)) = (i, lst) {
                match lst.as_ref() {
                    Value::List(lst) => Some(Rc::new(Value::RLeft(i, Rc::clone(lst)))),
                    _ => Some(Rc::new(Value::RLeft(i, Rc::new(vec![lst])))),
                }
            } else {
                None
            }
        }
        Reducer::RRight(lst, i) => {
            let i = exec_reducer(i, run_time);
            let lst = exec_reducer(lst, run_time);
            if let (Some(i), Some(lst)) = (i, lst) {
                match lst.as_ref() {
                    Value::List(lst) => Some(Rc::new(Value::RRight(i, Rc::clone(lst)))),
                    _ => Some(Rc::new(Value::RRight(i, Rc::new(vec![lst])))),
                }
            } else {
                None
            }
        }
        Reducer::Term(term) => exec_term(term, run_time),
    }
}

fn exec_term<'b>(term: &Term, run_time: &mut RunTime<'b>) -> Option<Rc<Value<'b>>> {
    match term {
        Term::Literal(literal) => exec_literal(literal, run_time),
        Term::List(list) => {
            let mut values = vec![];
            for item in list {
                if let Some(value) = exec_expr(item, run_time) {
                    values.push(value);
                } else {
                    return None;
                }
            }
            Some(Rc::new(Value::List(Rc::new(values))))
        }
        Term::Expr(exprs) => {
            let mut res = None;
            let mut run_time = run_time.clone();
            for expr in exprs {
                res = exec_expr(expr, &mut run_time);
            }
            res
        }
    }
}

fn exec_literal<'b>(literal: &Literal, run_time: &mut RunTime<'b>) -> Option<Rc<Value<'b>>> {
    match literal {
        Literal::Ident(ident) => {
            let ident = Rc::clone(&ident);
            if let Some(value) = run_time.env.get(&ident) {
                Some(Rc::clone(value))
            } else {
                None
            }
        }
        Literal::Num(num) => Some(Rc::new(Value::Num(*num))),
        Literal::Str(str) => Some(Rc::new(Value::Str(Rc::clone(str)))),
        _ => None,
    }
}

fn call_like_fnc_with_term<'b>(
    fnc: &Value<'b>,
    arg: &FncCall,
    run_time: &mut RunTime<'b>,
) -> Option<Rc<Value<'b>>> {
    match fnc {
        Value::List(..)
        | Value::Str(..)
        | Value::RLeft(..)
        | Value::RRight(..)
        | Value::Fnc(..) => match exec_fnc_call(arg, run_time) {
            Some(arg) => call_like_fnc_with_value(&fnc, arg, run_time),
            None => None,
        },
        Value::Num(v) => {
            let n = v.floor() as usize;
            let mut res = vec![];
            for _ in 0..n {
                if let Some(v) = exec_fnc_call(arg, run_time) {
                    res.push(v);
                } else {
                    return None;
                }
            }
            Some(Rc::new(Value::List(Rc::new(res))))
        }
        _ => None,
    }
}

fn call_like_fnc_with_value<'b>(
    fnc: &Value<'b>,
    arg: Rc<Value<'b>>,
    run_time: &mut RunTime<'b>,
) -> Option<Rc<Value<'b>>> {
    match fnc {
        Value::Fnc(fnc) => fnc(arg),
        Value::List(vs) => match arg.as_ref() {
            Value::Num(n) => {
                let n = n.floor();
                let idx = if n >= 0.0 {
                    n as usize
                } else {
                    vs.len() - (-n as usize)
                };
                vs.get(idx).map(|i| Rc::clone(i))
            }
            Value::Fnc(fnc) => {
                let mut rs = vec![];
                for v in vs.as_ref() {
                    if let Some(r) = fnc(Rc::clone(v)) {
                        rs.push(r);
                    }
                }
                Some(Rc::new(Value::List(Rc::new(rs))))
            }
            _ => None,
        },
        Value::Str(v) => match arg.as_ref() {
            Value::Num(n) => {
                let n = n.floor();
                let idx = if n >= 0.0 {
                    n as usize
                } else {
                    v.len() - (-n as usize)
                };
                v.as_str()
                    .chars()
                    .collect::<Vec<char>>()
                    .get(idx)
                    .map(|i| Rc::new(Value::Str(Rc::new(i.to_string()))))
            }
            _ => None,
        },
        Value::Num(v) => {
            let n = v.floor() as usize;
            let mut res = vec![];
            for _ in 0..n {
                res.push(Rc::clone(&arg));
            }
            Some(Rc::new(Value::List(Rc::new(res))))
        }
        Value::RLeft(i, lst) => {
            let mut pre = Rc::clone(i);
            for x in lst.iter() {
                if let Some(f) = call_like_fnc_with_value(&arg, Rc::clone(&pre), run_time) {
                    if let Some(p) = call_like_fnc_with_value(&f, Rc::clone(x), run_time) {
                        pre = p;
                    } else {
                        return None;
                    }
                } else {
                    return None;
                }
            }
            Some(pre)
        }
        Value::RRight(i, lst) => {
            let mut pre = Rc::clone(i);
            for x in lst.iter().rev() {
                if let Some(f) = call_like_fnc_with_value(&arg, Rc::clone(&pre), run_time) {
                    if let Some(p) = call_like_fnc_with_value(&f, Rc::clone(x), run_time) {
                        pre = p;
                    } else {
                        return None;
                    }
                } else {
                    return None;
                }
            }
            Some(pre)
        }
        _ => None,
    }
}

fn exec_expr_1_bool<'b>(left: bool, right: bool, op_code: &OpCode1) -> Option<Rc<Value<'b>>> {
    match op_code {
        OpCode1::Equal => Some(Rc::new(Value::Bool(left == right))),
        OpCode1::NotEq => Some(Rc::new(Value::Bool(left != right))),
        OpCode1::EqGreaterThan => Some(Rc::new(Value::Bool(left >= right))),
        OpCode1::EqLessThan => Some(Rc::new(Value::Bool(left <= right))),
        OpCode1::GreaterThan => Some(Rc::new(Value::Bool(left > right))),
        OpCode1::LessThan => Some(Rc::new(Value::Bool(left < right))),
    }
}

fn exec_expr_1_str<'b>(left: &String, right: &String, op_code: &OpCode1) -> Option<Rc<Value<'b>>> {
    match op_code {
        OpCode1::Equal => Some(Rc::new(Value::Bool(left == right))),
        OpCode1::NotEq => Some(Rc::new(Value::Bool(left != right))),
        OpCode1::EqGreaterThan => Some(Rc::new(Value::Bool(left >= right))),
        OpCode1::EqLessThan => Some(Rc::new(Value::Bool(left <= right))),
        OpCode1::GreaterThan => Some(Rc::new(Value::Bool(left > right))),
        OpCode1::LessThan => Some(Rc::new(Value::Bool(left < right))),
    }
}

fn exec_expr_1_num<'b>(left: f64, right: f64, op_code: &OpCode1) -> Option<Rc<Value<'b>>> {
    match op_code {
        OpCode1::Equal => Some(Rc::new(Value::Bool(left == right))),
        OpCode1::NotEq => Some(Rc::new(Value::Bool(left != right))),
        OpCode1::EqGreaterThan => Some(Rc::new(Value::Bool(left >= right))),
        OpCode1::EqLessThan => Some(Rc::new(Value::Bool(left <= right))),
        OpCode1::GreaterThan => Some(Rc::new(Value::Bool(left > right))),
        OpCode1::LessThan => Some(Rc::new(Value::Bool(left < right))),
    }
}

fn exec_expr_2_bool<'b>(left: bool, right: bool, op_code: &OpCode2) -> Option<Rc<Value<'b>>> {
    match op_code {
        OpCode2::Add => Some(Rc::new(Value::Bool(left || right))),
        _ => None,
    }
}

fn exec_expr_2_str<'b>(left: &String, right: &String, op_code: &OpCode2) -> Option<Rc<Value<'b>>> {
    match op_code {
        OpCode2::Add => Some(Rc::new(Value::Str(Rc::new(format!("{}{}", left, right))))),
        _ => None,
    }
}

fn exec_expr_2_num<'b>(left: f64, right: f64, op_code: &OpCode2) -> Option<Rc<Value<'b>>> {
    match op_code {
        OpCode2::Add => Some(Rc::new(Value::Num(left + right))),
        OpCode2::Sub => Some(Rc::new(Value::Num(left - right))),
    }
}

fn exec_expr_2_list<'b>(
    left: &Vec<Rc<Value<'b>>>,
    right: &Vec<Rc<Value<'b>>>,
    op_code: &OpCode2,
) -> Option<Rc<Value<'b>>> {
    match op_code {
        OpCode2::Add => Some(Rc::new(Value::List({
            let mut res = vec![];
            for i in left {
                res.push(Rc::clone(i));
            }
            for i in right {
                res.push(Rc::clone(i));
            }
            Rc::new(res)
        }))),
        OpCode2::Sub => None,
    }
}

fn exec_expr_3_bool<'b>(left: bool, right: bool, op_code: &OpCode3) -> Option<Rc<Value<'b>>> {
    match op_code {
        OpCode3::Multi => Some(Rc::new(Value::Bool(left && right))),
        _ => None,
    }
}

fn exec_expr_3_num<'b>(left: f64, right: f64, op_code: &OpCode3) -> Option<Rc<Value<'b>>> {
    match op_code {
        OpCode3::Multi => Some(Rc::new(Value::Num(left * right))),
        OpCode3::Div => Some(Rc::new(Value::Num(left / right))),
        OpCode3::Mod => Some(Rc::new(Value::Num(left % right))),
    }
}

fn exec_expr_4_num<'b>(
    left: f64,
    right: f64,
    op_code: &OpCode4,
    run_time: &mut RunTime,
) -> Option<Rc<Value<'b>>> {
    match op_code {
        OpCode4::SDice => Some(Rc::new(Value::Num(exec_dice(left, right, run_time) as f64))),
        OpCode4::LDice => {
            let mut res = vec![];
            let num = left.floor() as usize;
            for _ in 0..num {
                res.push(Rc::new(Value::Num(exec_dice(1.0, right, run_time))));
            }
            Some(Rc::new(Value::List(Rc::new(res))))
        }
    }
}

fn exec_dice(num: f64, a: f64, run_time: &mut RunTime) -> f64 {
    let num = num.floor() as usize;
    let a = a.floor() as u32;
    let mut res = 0;
    for _ in 0..num {
        res += (&mut *run_time.rand.borrow_mut())(a) + 1;
    }
    res as f64
}