mamba 0.3.6

A transpiler which converts Mamba files to Python 3 files
Documentation
use crate::{ASTTy, Context};
use crate::check::ast::NodeTy;
use crate::generate::ast::node::Core;
use crate::generate::convert::convert_node;
use crate::generate::convert::state::{Imports, State};
use crate::generate::result::{GenResult, UnimplementedErr};

pub fn convert_cntrl_flow(ast: &ASTTy, imp: &mut Imports, state: &State, ctx: &Context) -> GenResult {
    Ok(match &ast.node {
        NodeTy::IfElse { cond, then, el } => {
            let cond = Box::from(convert_node(cond, imp, &state.is_last_must_be_ret(false).must_assign_to(None, None), ctx)?);

            match el {
                Some(el) => if ast.ty.is_some() && is_valid_in_ternary(then, el) {
                    let state = state
                        .is_last_must_be_ret(false)
                        .remove_ret(true)
                        .must_assign_to(None, None);

                    Core::Ternary {
                        cond,
                        then: Box::from(convert_node(then, imp, &state, ctx)?),
                        el: Box::from(convert_node(el, imp, &state, ctx)?),
                    }
                } else {
                    Core::IfElse {
                        cond,
                        then: Box::from(convert_node(then, imp, state, ctx)?),
                        el: Box::from(convert_node(el, imp, state, ctx)?),
                    }
                }
                None => Core::If {
                    cond,
                    then: Box::from(convert_node(then, imp, state, ctx)?),
                },
            }
        }
        NodeTy::Match { cond, cases: match_cases } => {
            let expr = Box::from(convert_node(cond, imp, &state.is_last_must_be_ret(false).must_assign_to(None, None), ctx)?);

            let mut cases = vec![];
            for case in match_cases {
                if let NodeTy::Case { cond, body } = &case.node {
                    if let NodeTy::ExpressionType { expr, .. } = &cond.node {
                        cases.push(Core::Case {
                            expr: Box::from(convert_node(expr.as_ref(), imp, &state.is_last_must_be_ret(false).must_assign_to(None, None), ctx)?),
                            body: Box::from(convert_node(body.as_ref(), imp, state, ctx)?),
                        })
                    }
                }
            }

            Core::Match { expr, cases }
        }
        NodeTy::While { cond, body } => Core::While {
            cond: Box::from(convert_node(cond, imp, state, ctx)?),
            body: Box::from(convert_node(body, imp, state, ctx)?),
        },
        NodeTy::For { expr, col, body } => Core::For {
            expr: Box::from(convert_node(expr, imp, state, ctx)?),
            col: Box::from(convert_node(col, imp, state, ctx)?),
            body: Box::from(convert_node(body, imp, state, ctx)?),
        },
        NodeTy::Break => Core::Break,
        NodeTy::Continue => Core::Continue,
        other => {
            let msg = format!("Expected control flow but was: {other:?}.");
            return Err(Box::from(UnimplementedErr::new(ast, &msg)));
        }
    })
}

fn is_valid_in_ternary(then: &ASTTy, el: &ASTTy) -> bool {
    !matches!(then.node, NodeTy::Block { .. } | NodeTy::Raise { ..  })
        && !matches!(el.node, NodeTy::Block { .. } | NodeTy::Raise { ..  })
}

#[cfg(test)]
mod tests {
    use crate::ASTTy;
    use crate::common::position::Position;
    use crate::generate::ast::node::Core;
    use crate::generate::gen;
    use crate::parse::ast::AST;
    use crate::parse::ast::Node;

    macro_rules! to_pos_unboxed {
        ($node:expr) => {{
            AST { pos: Position::invisible(), node: $node }
        }};
    }

    macro_rules! to_pos {
        ($node:expr) => {{
            Box::from(to_pos_unboxed!($node))
        }};
    }

    #[test]
    fn if_verify() {
        let cond = to_pos!(Node::Id { lit: String::from("cond") });
        let then = to_pos!(Node::Id { lit: String::from("then") });
        let if_stmt = to_pos!(Node::IfElse { cond, then, el: None });

        let (core_cond, core_then) = match gen(&ASTTy::from(&if_stmt)) {
            Ok(Core::If { cond, then }) => (cond, then),
            other => panic!("Expected reassign but was {:?}", other),
        };

        assert_eq!(*core_cond, Core::Id { lit: String::from("cond") });
        assert_eq!(*core_then, Core::Id { lit: String::from("then") });
    }

    #[test]
    fn if_else_verify() {
        let cond = to_pos!(Node::Id { lit: String::from("cond") });
        let then = to_pos!(Node::Id { lit: String::from("then") });
        let el = to_pos!(Node::Id { lit: String::from("else") });
        let if_stmt = to_pos!(Node::IfElse { cond, then, el: Some(el) });

        let (core_cond, core_then, core_else) = match gen(&ASTTy::from(&if_stmt)) {
            Ok(Core::IfElse { cond, then, el }) => (cond, then, el),
            other => panic!("Expected reassign but was {:?}", other),
        };

        assert_eq!(*core_cond, Core::Id { lit: String::from("cond") });
        assert_eq!(*core_then, Core::Id { lit: String::from("then") });
        assert_eq!(*core_else, Core::Id { lit: String::from("else") });
    }

    #[test]
    fn while_verify() {
        let cond = to_pos!(Node::Id { lit: String::from("cond") });
        let body = to_pos!(Node::ENum { num: String::from("num"), exp: String::from("") });
        let while_stmt = to_pos!(Node::While { cond, body });

        let (core_cond, core_body) = match gen(&ASTTy::from(&while_stmt)) {
            Ok(Core::While { cond, body }) => (cond, body),
            other => panic!("Expected reassign but was {:?}", other),
        };

        assert_eq!(*core_cond, Core::Id { lit: String::from("cond") });
        assert_eq!(*core_body, Core::ENum { num: String::from("num"), exp: String::from("0") });
    }

    #[test]
    fn for_verify() {
        let expr = to_pos!(Node::Id { lit: String::from("expr_1") });
        let col = to_pos!(Node::Id { lit: String::from("col") });
        let body = to_pos!(Node::Id { lit: String::from("body") });
        let for_stmt = to_pos!(Node::For { expr, col, body });

        let (core_expr, core_col, core_body) = match gen(&ASTTy::from(&for_stmt)) {
            Ok(Core::For { expr, col, body }) => (expr, col, body),
            other => panic!("Expected for but was {:?}", other),
        };

        assert_eq!(*core_expr, Core::Id { lit: String::from("expr_1") });
        assert_eq!(*core_col, Core::Id { lit: String::from("col") });
        assert_eq!(*core_body, Core::Id { lit: String::from("body") });
    }

    #[test]
    fn range_verify() {
        let from = to_pos!(Node::Id { lit: String::from("a") });
        let to = to_pos!(Node::Id { lit: String::from("b") });
        let range = to_pos!(Node::Range { from, to, inclusive: false, step: None });

        let (from, to, step) = match gen(&ASTTy::from(&range)) {
            Ok(Core::FunctionCall { function, args }) => {
                assert_eq!(*function, Core::Id { lit: String::from("range") });
                (args[0].clone(), args[1].clone(), args[2].clone())
            }
            other => panic!("Expected range but was {:?}", other),
        };

        assert_eq!(from, Core::Id { lit: String::from("a") });
        assert_eq!(to, Core::Id { lit: String::from("b") });
        assert_eq!(step, Core::Int { int: String::from("1") });
    }

    #[test]
    fn range_incl_verify() {
        let from = to_pos!(Node::Id { lit: String::from("a") });
        let to = to_pos!(Node::Id { lit: String::from("b") });
        let range = to_pos!(Node::Range { from, to, inclusive: true, step: None });

        let (from, to, step) = match gen(&ASTTy::from(&range)) {
            Ok(Core::FunctionCall { function, args }) => {
                assert_eq!(*function, Core::Id { lit: String::from("range") });
                (args[0].clone(), args[1].clone(), args[2].clone())
            }
            other => panic!("Expected range but was {:?}", other),
        };

        assert_eq!(from, Core::Id { lit: String::from("a") });
        assert_eq!(
            to,
            Core::Add {
                left: Box::from(Core::Id { lit: String::from("b") }),
                right: Box::from(Core::Int { int: String::from("1") }),
            }
        );
        assert_eq!(step, Core::Int { int: String::from("1") });
    }

    #[test]
    fn range_step_verify() {
        let from = to_pos!(Node::Id { lit: String::from("a") });
        let to = to_pos!(Node::Id { lit: String::from("b") });
        let step = Some(to_pos!(Node::Id { lit: String::from("c") }));
        let range = to_pos!(Node::Range { from, to, inclusive: false, step });

        let (from, to, step) = match gen(&ASTTy::from(&range)) {
            Ok(Core::FunctionCall { function, args }) => {
                assert_eq!(*function, Core::Id { lit: String::from("range") });
                (args[0].clone(), args[1].clone(), args[2].clone())
            }
            other => panic!("Expected range but was {:?}", other),
        };

        assert_eq!(from, Core::Id { lit: String::from("a") });
        assert_eq!(to, Core::Id { lit: String::from("b") });
        assert_eq!(step, Core::Id { lit: String::from("c") });
    }
}