mamba 0.3.6

A transpiler which converts Mamba files to Python 3 files
Documentation
use crate::parse::ast::AST;
use crate::parse::ast::Node;
use crate::parse::definition::parse_fun_arg;
use crate::parse::expression::parse_inner_expression;
use crate::parse::iterator::LexIterator;
use crate::parse::lex::token::Token;
use crate::parse::operation::parse_expression;
use crate::parse::result::{expected_one_of, ParseResult};

pub fn parse_anon_fun(it: &mut LexIterator) -> ParseResult {
    let start = it.start_pos("anonymous function")?;
    it.eat(&Token::BSlash, "anonymous function")?;

    let mut args: Vec<AST> = vec![];
    it.peek_while_not_token(&Token::BTo, &mut |it, _| {
        args.push(*it.parse(&parse_fun_arg, "anonymous function", start)?);
        it.eat_if(&Token::Comma);
        Ok(())
    })?;

    it.eat(&Token::BTo, "anonymous function")?;

    let body = it.parse(&parse_expression, "anonymous function", start)?;
    let node = Node::AnonFun { args, body: body.clone() };
    Ok(Box::from(AST::new(start.union(body.pos), node)))
}

pub fn parse_call(pre: &AST, it: &mut LexIterator) -> ParseResult {
    it.peek_or_err(
        &|it, ast| match ast.token {
            Token::Point => {
                it.eat(&Token::Point, "call")?;
                let property = it.parse(&parse_inner_expression, "call", pre.pos)?;
                let node = Node::PropertyCall {
                    instance: Box::from(pre.clone()),
                    property: property.clone(),
                };
                Ok(Box::from(AST::new(pre.pos.union(property.pos), node)))
            }
            Token::LRBrack => {
                it.eat(&Token::LRBrack, "direct call")?;
                let args = it.parse_vec(&parse_arguments, "direct call", pre.pos)?;
                let end = it.eat(&Token::RRBrack, "direct call")?;
                let node = Node::FunctionCall { name: Box::from(pre.clone()), args };
                Ok(Box::from(AST::new(pre.pos.union(end), node)))
            }
            _ => Err(Box::from(expected_one_of(&[Token::Point, Token::LRBrack], ast, "function call"))),
        },
        &[Token::Point, Token::LRBrack],
        "function call",
    )
}

fn parse_arguments(it: &mut LexIterator) -> ParseResult<Vec<AST>> {
    let start = it.start_pos("arguments")?;
    let mut arguments = vec![];
    it.peek_while_not_token(&Token::RRBrack, &mut |it, _| {
        arguments.push(*it.parse(&parse_expression, "arguments", start)?);
        it.eat_if(&Token::Comma);
        Ok(())
    })?;
    Ok(arguments)
}

#[cfg(test)]
mod test {
    use crate::parse::{parse, parse_direct};
    use crate::parse::ast::{AST, Node};
    use crate::parse::ast::node_op::NodeOp;

    #[test]
    fn op_assign() {
        let source = String::from("a:=1\nb+=2\nc-=3\nd*=4\ne/=5\nf^=6\ng<<=7\nh>>=8\n");
        let statements = parse_direct(&source).unwrap();

        let ops: Vec<NodeOp> = statements
            .iter()
            .map(|ast| match &ast.node {
                Node::Reassign { op, .. } => op.clone(),
                other => panic!("Expected reassign {:?}", other)
            })
            .collect();

        assert_eq!(ops[0], NodeOp::Assign);
        assert_eq!(ops[1], NodeOp::Add);
        assert_eq!(ops[2], NodeOp::Sub);
        assert_eq!(ops[3], NodeOp::Mul);
        assert_eq!(ops[4], NodeOp::Div);
        assert_eq!(ops[5], NodeOp::Pow);
        assert_eq!(ops[6], NodeOp::BLShift);
        assert_eq!(ops[7], NodeOp::BRShift);
    }

    #[test]
    fn anon_fun_no_args_verify() {
        let source = String::from("\\ => c");
        let statements = parse_direct(&source).unwrap();

        let Node::AnonFun { args, body } = &statements.first().expect("script empty.").node else {
            panic!("first element script was anon fun.")
        };

        assert_eq!(args.len(), 0);
        assert_eq!(body.node, Node::Id { lit: String::from("c") });
    }

    #[test]
    fn anon_fun_verify() {
        let source = String::from("\\a,b => c");
        let statements = parse_direct(&source).unwrap();

        let Node::AnonFun { args, body } = &statements.first().expect("script empty.").node else {
            panic!("first element script was anon fun.")
        };

        assert_eq!(args.len(), 2);
        let (id1, id2) = match (&args[0], &args[1]) {
            (
                AST { node: Node::FunArg { var: id1, ty: None, mutable: true, .. }, .. },
                AST { node: Node::FunArg { var: id2, ty: None, mutable: true, .. }, .. },
            ) => (id1.clone(), id2.clone()),
            other => panic!("Id's of anon fun not expression type: {:?}", other),
        };

        assert_eq!(id1.node, Node::Id { lit: String::from("a") });
        assert_eq!(id2.node, Node::Id { lit: String::from("b") });

        assert_eq!(body.node, Node::Id { lit: String::from("c") });
    }

    #[test]
    fn direct_call_verify() {
        let source = String::from("a(b, c)");
        let statements = parse_direct(&source).unwrap();

        let Node::FunctionCall { name, args } = &statements.first().expect("script empty.").node else {
            panic!("first element script was anon fun.")
        };

        assert_eq!(name.node, Node::Id { lit: String::from("a") });
        assert_eq!(args.len(), 2);
        assert_eq!(args[0].node, Node::Id { lit: String::from("b") });
        assert_eq!(args[1].node, Node::Id { lit: String::from("c") });
    }

    #[test]
    fn method_call_verify() {
        let source = String::from("instance.a(b, c)");
        let statements = parse_direct(&source).unwrap();

        let (instance, name, args) = match &statements.first().expect("script empty.").node {
            Node::PropertyCall { instance, property } => match &property.node {
                Node::FunctionCall { name, args } => (instance.clone(), name.clone(), args.clone()),
                other => panic!("not function call in property call {:?}", other),
            },
            other => panic!("first element script was property call {:?}", other),
        };

        assert_eq!(instance.node, Node::Id { lit: String::from("instance") });
        assert_eq!(name.node, Node::Id { lit: String::from("a") });

        assert_eq!(args.len(), 2);
        assert_eq!(args[0].node, Node::Id { lit: String::from("b") });
        assert_eq!(args[1].node, Node::Id { lit: String::from("c") });
    }

    #[test]
    fn direct_call_missing_closing_bracket() {
        let source = String::from("a(b");
        parse(&source).unwrap_err();
    }

    #[test]
    fn regular_call_missing_closing_bracket() {
        let source = String::from("instance.a(b");
        parse(&source).unwrap_err();
    }
}