Factor out expr parser into its own module
This commit is contained in:
		
							parent
							
								
									1ea2d8ba9f
								
							
						
					
					
						commit
						3dff189499
					
				
					 4 changed files with 510 additions and 495 deletions
				
			
		| 
						 | 
				
			
			@ -6,6 +6,7 @@ pub(crate) mod commands;
 | 
			
		|||
pub(crate) mod common;
 | 
			
		||||
pub mod compiler;
 | 
			
		||||
pub mod interpreter;
 | 
			
		||||
#[macro_use]
 | 
			
		||||
pub mod parser;
 | 
			
		||||
 | 
			
		||||
pub use common::{Error, Result};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										482
									
								
								src/parser/expr.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										482
									
								
								src/parser/expr.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,482 @@
 | 
			
		|||
use nom::character::complete::{digit1, multispace0, multispace1};
 | 
			
		||||
use nom::{
 | 
			
		||||
    alt, char, complete, delimited, do_parse, flat_map, many0, map, named, parse_to,
 | 
			
		||||
    separated_list0, separated_list1, tag, tuple,
 | 
			
		||||
};
 | 
			
		||||
use pratt::{Affix, Associativity, PrattParser, Precedence};
 | 
			
		||||
 | 
			
		||||
use crate::ast::{BinaryOperator, Expr, Fun, Ident, Literal, UnaryOperator};
 | 
			
		||||
use crate::parser::ident;
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
enum TokenTree<'a> {
 | 
			
		||||
    Prefix(UnaryOperator),
 | 
			
		||||
    // Postfix(char),
 | 
			
		||||
    Infix(BinaryOperator),
 | 
			
		||||
    Primary(Expr<'a>),
 | 
			
		||||
    Group(Vec<TokenTree<'a>>),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
named!(prefix(&str) -> TokenTree, map!(alt!(
 | 
			
		||||
    complete!(char!('-')) => { |_| UnaryOperator::Neg } |
 | 
			
		||||
    complete!(char!('!')) => { |_| UnaryOperator::Not }
 | 
			
		||||
), TokenTree::Prefix));
 | 
			
		||||
 | 
			
		||||
named!(infix(&str) -> TokenTree, map!(alt!(
 | 
			
		||||
    complete!(tag!("==")) => { |_| BinaryOperator::Equ } |
 | 
			
		||||
    complete!(tag!("!=")) => { |_| BinaryOperator::Neq } |
 | 
			
		||||
    complete!(char!('+')) => { |_| BinaryOperator::Add } |
 | 
			
		||||
    complete!(char!('-')) => { |_| BinaryOperator::Sub } |
 | 
			
		||||
    complete!(char!('*')) => { |_| BinaryOperator::Mul } |
 | 
			
		||||
    complete!(char!('/')) => { |_| BinaryOperator::Div } |
 | 
			
		||||
    complete!(char!('^')) => { |_| BinaryOperator::Pow }
 | 
			
		||||
), TokenTree::Infix));
 | 
			
		||||
 | 
			
		||||
named!(primary(&str) -> TokenTree, alt!(
 | 
			
		||||
    do_parse!(
 | 
			
		||||
        multispace0 >>
 | 
			
		||||
        char!('(') >>
 | 
			
		||||
        multispace0 >>
 | 
			
		||||
        group: group >>
 | 
			
		||||
        multispace0 >>
 | 
			
		||||
        char!(')') >>
 | 
			
		||||
        multispace0 >>
 | 
			
		||||
            (TokenTree::Group(group))
 | 
			
		||||
    ) |
 | 
			
		||||
    delimited!(multispace0, simple_expr, multispace0) => { |s| TokenTree::Primary(s) }
 | 
			
		||||
));
 | 
			
		||||
 | 
			
		||||
named!(
 | 
			
		||||
    rest(&str) -> Vec<(TokenTree, Vec<TokenTree>, TokenTree)>,
 | 
			
		||||
    many0!(tuple!(
 | 
			
		||||
        infix,
 | 
			
		||||
        delimited!(multispace0, many0!(prefix), multispace0),
 | 
			
		||||
        primary
 | 
			
		||||
        // many0!(postfix)
 | 
			
		||||
    ))
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
named!(group(&str) -> Vec<TokenTree>, do_parse!(
 | 
			
		||||
    prefix: many0!(prefix)
 | 
			
		||||
        >> primary: primary
 | 
			
		||||
        // >> postfix: many0!(postfix)
 | 
			
		||||
        >> rest: rest
 | 
			
		||||
        >> ({
 | 
			
		||||
            let mut res = prefix;
 | 
			
		||||
            res.push(primary);
 | 
			
		||||
            // res.append(&mut postfix);
 | 
			
		||||
            for (infix, mut prefix, primary/*, mut postfix*/) in rest {
 | 
			
		||||
                res.push(infix);
 | 
			
		||||
                res.append(&mut prefix);
 | 
			
		||||
                res.push(primary);
 | 
			
		||||
                // res.append(&mut postfix);
 | 
			
		||||
            }
 | 
			
		||||
            res
 | 
			
		||||
        })
 | 
			
		||||
));
 | 
			
		||||
 | 
			
		||||
fn token_tree(i: &str) -> nom::IResult<&str, Vec<TokenTree>> {
 | 
			
		||||
    group(i)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct ExprParser;
 | 
			
		||||
 | 
			
		||||
impl<'a, I> PrattParser<I> for ExprParser
 | 
			
		||||
where
 | 
			
		||||
    I: Iterator<Item = TokenTree<'a>>,
 | 
			
		||||
{
 | 
			
		||||
    type Error = pratt::NoError;
 | 
			
		||||
    type Input = TokenTree<'a>;
 | 
			
		||||
    type Output = Expr<'a>;
 | 
			
		||||
 | 
			
		||||
    fn query(&mut self, input: &Self::Input) -> Result<Affix, Self::Error> {
 | 
			
		||||
        use BinaryOperator::*;
 | 
			
		||||
        use UnaryOperator::*;
 | 
			
		||||
 | 
			
		||||
        Ok(match input {
 | 
			
		||||
            TokenTree::Infix(Add) => Affix::Infix(Precedence(6), Associativity::Left),
 | 
			
		||||
            TokenTree::Infix(Sub) => Affix::Infix(Precedence(6), Associativity::Left),
 | 
			
		||||
            TokenTree::Infix(Mul) => Affix::Infix(Precedence(7), Associativity::Left),
 | 
			
		||||
            TokenTree::Infix(Div) => Affix::Infix(Precedence(7), Associativity::Left),
 | 
			
		||||
            TokenTree::Infix(Pow) => Affix::Infix(Precedence(8), Associativity::Right),
 | 
			
		||||
            TokenTree::Infix(Equ) => Affix::Infix(Precedence(4), Associativity::Right),
 | 
			
		||||
            TokenTree::Infix(Neq) => Affix::Infix(Precedence(4), Associativity::Right),
 | 
			
		||||
            TokenTree::Prefix(Neg) => Affix::Prefix(Precedence(6)),
 | 
			
		||||
            TokenTree::Prefix(Not) => Affix::Prefix(Precedence(6)),
 | 
			
		||||
            TokenTree::Primary(_) => Affix::Nilfix,
 | 
			
		||||
            TokenTree::Group(_) => Affix::Nilfix,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn primary(&mut self, input: Self::Input) -> Result<Self::Output, Self::Error> {
 | 
			
		||||
        Ok(match input {
 | 
			
		||||
            TokenTree::Primary(expr) => expr,
 | 
			
		||||
            TokenTree::Group(group) => self.parse(&mut group.into_iter()).unwrap(),
 | 
			
		||||
            _ => unreachable!(),
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn infix(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        lhs: Self::Output,
 | 
			
		||||
        op: Self::Input,
 | 
			
		||||
        rhs: Self::Output,
 | 
			
		||||
    ) -> Result<Self::Output, Self::Error> {
 | 
			
		||||
        let op = match op {
 | 
			
		||||
            TokenTree::Infix(op) => op,
 | 
			
		||||
            _ => unreachable!(),
 | 
			
		||||
        };
 | 
			
		||||
        Ok(Expr::BinaryOp {
 | 
			
		||||
            lhs: Box::new(lhs),
 | 
			
		||||
            op,
 | 
			
		||||
            rhs: Box::new(rhs),
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn prefix(&mut self, op: Self::Input, rhs: Self::Output) -> Result<Self::Output, Self::Error> {
 | 
			
		||||
        let op = match op {
 | 
			
		||||
            TokenTree::Prefix(op) => op,
 | 
			
		||||
            _ => unreachable!(),
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        Ok(Expr::UnaryOp {
 | 
			
		||||
            op,
 | 
			
		||||
            rhs: Box::new(rhs),
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn postfix(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        _lhs: Self::Output,
 | 
			
		||||
        _op: Self::Input,
 | 
			
		||||
    ) -> Result<Self::Output, Self::Error> {
 | 
			
		||||
        unreachable!()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
named!(int(&str) -> Literal, map!(flat_map!(digit1, parse_to!(u64)), Literal::Int));
 | 
			
		||||
 | 
			
		||||
named!(literal(&str) -> Expr, map!(alt!(int), Expr::Literal));
 | 
			
		||||
 | 
			
		||||
named!(binding(&str) -> (Ident, Expr), do_parse!(
 | 
			
		||||
    multispace0
 | 
			
		||||
        >> ident: ident
 | 
			
		||||
        >> multispace0
 | 
			
		||||
        >> char!('=')
 | 
			
		||||
        >> multispace0
 | 
			
		||||
        >> expr: expr
 | 
			
		||||
        >> (ident, expr)
 | 
			
		||||
));
 | 
			
		||||
 | 
			
		||||
named!(let_(&str) -> Expr, do_parse!(
 | 
			
		||||
    tag!("let")
 | 
			
		||||
        >> multispace0
 | 
			
		||||
        >> bindings: separated_list1!(alt!(char!(';') | char!('\n')), binding)
 | 
			
		||||
        >> multispace0
 | 
			
		||||
        >> tag!("in")
 | 
			
		||||
        >> multispace0
 | 
			
		||||
        >> body: expr
 | 
			
		||||
        >> (Expr::Let {
 | 
			
		||||
            bindings,
 | 
			
		||||
            body: Box::new(body)
 | 
			
		||||
        })
 | 
			
		||||
));
 | 
			
		||||
 | 
			
		||||
named!(if_(&str) -> Expr, do_parse! (
 | 
			
		||||
    tag!("if")
 | 
			
		||||
        >> multispace0
 | 
			
		||||
        >> condition: expr
 | 
			
		||||
        >> multispace0
 | 
			
		||||
        >> tag!("then")
 | 
			
		||||
        >> multispace0
 | 
			
		||||
        >> then: expr
 | 
			
		||||
        >> multispace0
 | 
			
		||||
        >> tag!("else")
 | 
			
		||||
        >> multispace0
 | 
			
		||||
        >> else_: expr
 | 
			
		||||
        >> (Expr::If {
 | 
			
		||||
            condition: Box::new(condition),
 | 
			
		||||
            then: Box::new(then),
 | 
			
		||||
            else_: Box::new(else_)
 | 
			
		||||
        })
 | 
			
		||||
));
 | 
			
		||||
 | 
			
		||||
named!(ident_expr(&str) -> Expr, map!(ident, Expr::Ident));
 | 
			
		||||
 | 
			
		||||
named!(paren_expr(&str) -> Expr,
 | 
			
		||||
       delimited!(complete!(tag!("(")), expr, complete!(tag!(")"))));
 | 
			
		||||
 | 
			
		||||
named!(funcref(&str) -> Expr, alt!(
 | 
			
		||||
    ident_expr |
 | 
			
		||||
    paren_expr
 | 
			
		||||
));
 | 
			
		||||
 | 
			
		||||
named!(no_arg_call(&str) -> Expr, do_parse!(
 | 
			
		||||
    fun: funcref
 | 
			
		||||
        >> multispace0
 | 
			
		||||
        >> complete!(tag!("()"))
 | 
			
		||||
        >> (Expr::Call {
 | 
			
		||||
            fun: Box::new(fun),
 | 
			
		||||
            args: vec![],
 | 
			
		||||
        })
 | 
			
		||||
));
 | 
			
		||||
 | 
			
		||||
named!(fun_expr(&str) -> Expr, do_parse!(
 | 
			
		||||
    tag!("fn")
 | 
			
		||||
        >> multispace1
 | 
			
		||||
        >> args: separated_list0!(multispace1, ident)
 | 
			
		||||
        >> multispace0
 | 
			
		||||
        >> char!('=')
 | 
			
		||||
        >> multispace0
 | 
			
		||||
        >> body: expr
 | 
			
		||||
        >> (Expr::Fun(Box::new(Fun {
 | 
			
		||||
            args,
 | 
			
		||||
            body
 | 
			
		||||
        })))
 | 
			
		||||
));
 | 
			
		||||
 | 
			
		||||
named!(arg(&str) -> Expr, alt!(
 | 
			
		||||
    ident_expr |
 | 
			
		||||
    literal |
 | 
			
		||||
    paren_expr
 | 
			
		||||
));
 | 
			
		||||
 | 
			
		||||
named!(call_with_args(&str) -> Expr, do_parse!(
 | 
			
		||||
    fun: funcref
 | 
			
		||||
        >> multispace1
 | 
			
		||||
        >> args: separated_list1!(multispace1, arg)
 | 
			
		||||
        >> (Expr::Call {
 | 
			
		||||
            fun: Box::new(fun),
 | 
			
		||||
            args
 | 
			
		||||
        })
 | 
			
		||||
));
 | 
			
		||||
 | 
			
		||||
named!(simple_expr(&str) -> Expr, alt!(
 | 
			
		||||
    let_ |
 | 
			
		||||
    if_ |
 | 
			
		||||
    fun_expr |
 | 
			
		||||
    literal |
 | 
			
		||||
    ident_expr
 | 
			
		||||
));
 | 
			
		||||
 | 
			
		||||
named!(pub expr(&str) -> Expr, alt!(
 | 
			
		||||
    no_arg_call |
 | 
			
		||||
    call_with_args |
 | 
			
		||||
    map!(token_tree, |tt| {
 | 
			
		||||
        ExprParser.parse(&mut tt.into_iter()).unwrap()
 | 
			
		||||
    }) |
 | 
			
		||||
    simple_expr));
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
pub(crate) mod tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use std::convert::TryFrom;
 | 
			
		||||
    use BinaryOperator::*;
 | 
			
		||||
    use Expr::{BinaryOp, If, Let, UnaryOp};
 | 
			
		||||
    use UnaryOperator::*;
 | 
			
		||||
 | 
			
		||||
    pub(crate) fn ident_expr(s: &str) -> Box<Expr> {
 | 
			
		||||
        Box::new(Expr::Ident(Ident::try_from(s).unwrap()))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    mod operators {
 | 
			
		||||
        use super::*;
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn mul_plus() {
 | 
			
		||||
            let (rem, res) = expr("x*y+z").unwrap();
 | 
			
		||||
            assert!(rem.is_empty());
 | 
			
		||||
            assert_eq!(
 | 
			
		||||
                res,
 | 
			
		||||
                BinaryOp {
 | 
			
		||||
                    lhs: Box::new(BinaryOp {
 | 
			
		||||
                        lhs: ident_expr("x"),
 | 
			
		||||
                        op: Mul,
 | 
			
		||||
                        rhs: ident_expr("y")
 | 
			
		||||
                    }),
 | 
			
		||||
                    op: Add,
 | 
			
		||||
                    rhs: ident_expr("z")
 | 
			
		||||
                }
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn mul_plus_ws() {
 | 
			
		||||
            let (rem, res) = expr("x * y    +    z").unwrap();
 | 
			
		||||
            assert!(rem.is_empty(), "non-empty remainder: \"{}\"", rem);
 | 
			
		||||
            assert_eq!(
 | 
			
		||||
                res,
 | 
			
		||||
                BinaryOp {
 | 
			
		||||
                    lhs: Box::new(BinaryOp {
 | 
			
		||||
                        lhs: ident_expr("x"),
 | 
			
		||||
                        op: Mul,
 | 
			
		||||
                        rhs: ident_expr("y")
 | 
			
		||||
                    }),
 | 
			
		||||
                    op: Add,
 | 
			
		||||
                    rhs: ident_expr("z")
 | 
			
		||||
                }
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn unary() {
 | 
			
		||||
            let (rem, res) = expr("x * -z").unwrap();
 | 
			
		||||
            assert!(rem.is_empty(), "non-empty remainder: \"{}\"", rem);
 | 
			
		||||
            assert_eq!(
 | 
			
		||||
                res,
 | 
			
		||||
                BinaryOp {
 | 
			
		||||
                    lhs: ident_expr("x"),
 | 
			
		||||
                    op: Mul,
 | 
			
		||||
                    rhs: Box::new(UnaryOp {
 | 
			
		||||
                        op: Neg,
 | 
			
		||||
                        rhs: ident_expr("z"),
 | 
			
		||||
                    })
 | 
			
		||||
                }
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn mul_literal() {
 | 
			
		||||
            let (rem, res) = expr("x * 3").unwrap();
 | 
			
		||||
            assert!(rem.is_empty());
 | 
			
		||||
            assert_eq!(
 | 
			
		||||
                res,
 | 
			
		||||
                BinaryOp {
 | 
			
		||||
                    lhs: ident_expr("x"),
 | 
			
		||||
                    op: Mul,
 | 
			
		||||
                    rhs: Box::new(Expr::Literal(Literal::Int(3))),
 | 
			
		||||
                }
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn equ() {
 | 
			
		||||
            let res = test_parse!(expr, "x * 7 == 7");
 | 
			
		||||
            assert_eq!(
 | 
			
		||||
                res,
 | 
			
		||||
                BinaryOp {
 | 
			
		||||
                    lhs: Box::new(BinaryOp {
 | 
			
		||||
                        lhs: ident_expr("x"),
 | 
			
		||||
                        op: Mul,
 | 
			
		||||
                        rhs: Box::new(Expr::Literal(Literal::Int(7)))
 | 
			
		||||
                    }),
 | 
			
		||||
                    op: Equ,
 | 
			
		||||
                    rhs: Box::new(Expr::Literal(Literal::Int(7)))
 | 
			
		||||
                }
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn let_complex() {
 | 
			
		||||
        let res = test_parse!(expr, "let x = 1; y = x * 7 in (x + y) * 4");
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            res,
 | 
			
		||||
            Let {
 | 
			
		||||
                bindings: vec![
 | 
			
		||||
                    (
 | 
			
		||||
                        Ident::try_from("x").unwrap(),
 | 
			
		||||
                        Expr::Literal(Literal::Int(1))
 | 
			
		||||
                    ),
 | 
			
		||||
                    (
 | 
			
		||||
                        Ident::try_from("y").unwrap(),
 | 
			
		||||
                        Expr::BinaryOp {
 | 
			
		||||
                            lhs: ident_expr("x"),
 | 
			
		||||
                            op: Mul,
 | 
			
		||||
                            rhs: Box::new(Expr::Literal(Literal::Int(7)))
 | 
			
		||||
                        }
 | 
			
		||||
                    )
 | 
			
		||||
                ],
 | 
			
		||||
                body: Box::new(Expr::BinaryOp {
 | 
			
		||||
                    lhs: Box::new(Expr::BinaryOp {
 | 
			
		||||
                        lhs: ident_expr("x"),
 | 
			
		||||
                        op: Add,
 | 
			
		||||
                        rhs: ident_expr("y"),
 | 
			
		||||
                    }),
 | 
			
		||||
                    op: Mul,
 | 
			
		||||
                    rhs: Box::new(Expr::Literal(Literal::Int(4))),
 | 
			
		||||
                })
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn if_simple() {
 | 
			
		||||
        let res = test_parse!(expr, "if x == 8 then 9 else 20");
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            res,
 | 
			
		||||
            If {
 | 
			
		||||
                condition: Box::new(BinaryOp {
 | 
			
		||||
                    lhs: ident_expr("x"),
 | 
			
		||||
                    op: Equ,
 | 
			
		||||
                    rhs: Box::new(Expr::Literal(Literal::Int(8))),
 | 
			
		||||
                }),
 | 
			
		||||
                then: Box::new(Expr::Literal(Literal::Int(9))),
 | 
			
		||||
                else_: Box::new(Expr::Literal(Literal::Int(20)))
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn no_arg_call() {
 | 
			
		||||
        let res = test_parse!(expr, "f()");
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            res,
 | 
			
		||||
            Expr::Call {
 | 
			
		||||
                fun: ident_expr("f"),
 | 
			
		||||
                args: vec![]
 | 
			
		||||
            }
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn call_with_args() {
 | 
			
		||||
        let res = test_parse!(expr, "f x 1");
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            res,
 | 
			
		||||
            Expr::Call {
 | 
			
		||||
                fun: ident_expr("f"),
 | 
			
		||||
                args: vec![*ident_expr("x"), Expr::Literal(Literal::Int(1))]
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn call_funcref() {
 | 
			
		||||
        let res = test_parse!(expr, "(let x = 1 in x) 2");
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            res,
 | 
			
		||||
            Expr::Call {
 | 
			
		||||
                fun: Box::new(Expr::Let {
 | 
			
		||||
                    bindings: vec![(
 | 
			
		||||
                        Ident::try_from("x").unwrap(),
 | 
			
		||||
                        Expr::Literal(Literal::Int(1))
 | 
			
		||||
                    )],
 | 
			
		||||
                    body: ident_expr("x")
 | 
			
		||||
                }),
 | 
			
		||||
                args: vec![Expr::Literal(Literal::Int(2))]
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn anon_function() {
 | 
			
		||||
        let res = test_parse!(expr, "let id = fn x = x in id 1");
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            res,
 | 
			
		||||
            Expr::Let {
 | 
			
		||||
                bindings: vec![(
 | 
			
		||||
                    Ident::try_from("id").unwrap(),
 | 
			
		||||
                    Expr::Fun(Box::new(Fun {
 | 
			
		||||
                        args: vec![Ident::try_from("x").unwrap()],
 | 
			
		||||
                        body: *ident_expr("x")
 | 
			
		||||
                    }))
 | 
			
		||||
                )],
 | 
			
		||||
                body: Box::new(Expr::Call {
 | 
			
		||||
                    fun: ident_expr("id"),
 | 
			
		||||
                    args: vec![Expr::Literal(Literal::Int(1))],
 | 
			
		||||
                })
 | 
			
		||||
            }
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										15
									
								
								src/parser/macros.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/parser/macros.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,15 @@
 | 
			
		|||
#[macro_use]
 | 
			
		||||
macro_rules! test_parse {
 | 
			
		||||
    ($parser: ident, $src: expr) => {{
 | 
			
		||||
        let res = $parser($src);
 | 
			
		||||
        nom_trace::print_trace!();
 | 
			
		||||
        let (rem, res) = res.unwrap();
 | 
			
		||||
        assert!(
 | 
			
		||||
            rem.is_empty(),
 | 
			
		||||
            "non-empty remainder: \"{}\", parsed: {:?}",
 | 
			
		||||
            rem,
 | 
			
		||||
            res
 | 
			
		||||
        );
 | 
			
		||||
        res
 | 
			
		||||
    }};
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,166 +1,21 @@
 | 
			
		|||
use nom::character::complete::{digit1, multispace0, multispace1};
 | 
			
		||||
use nom::character::complete::{multispace0, multispace1};
 | 
			
		||||
use nom::error::{ErrorKind, ParseError};
 | 
			
		||||
use nom::{
 | 
			
		||||
    alt, char, complete, delimited, do_parse, flat_map, many0, map, named, parse_to,
 | 
			
		||||
    separated_list0, separated_list1, tag, tuple,
 | 
			
		||||
};
 | 
			
		||||
use pratt::{Affix, Associativity, PrattParser, Precedence};
 | 
			
		||||
use nom::{alt, char, complete, do_parse, many0, named, separated_list0, tag};
 | 
			
		||||
 | 
			
		||||
use crate::ast::{BinaryOperator, Decl, Expr, Fun, Ident, Literal, UnaryOperator};
 | 
			
		||||
#[macro_use]
 | 
			
		||||
mod macros;
 | 
			
		||||
mod expr;
 | 
			
		||||
 | 
			
		||||
use crate::ast::{Decl, Fun, Ident};
 | 
			
		||||
pub use expr::expr;
 | 
			
		||||
 | 
			
		||||
pub type Error = nom::Err<nom::error::Error<String>>;
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
enum TokenTree<'a> {
 | 
			
		||||
    Prefix(UnaryOperator),
 | 
			
		||||
    // Postfix(char),
 | 
			
		||||
    Infix(BinaryOperator),
 | 
			
		||||
    Primary(Expr<'a>),
 | 
			
		||||
    Group(Vec<TokenTree<'a>>),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
named!(prefix(&str) -> TokenTree, map!(alt!(
 | 
			
		||||
    complete!(char!('-')) => { |_| UnaryOperator::Neg } |
 | 
			
		||||
    complete!(char!('!')) => { |_| UnaryOperator::Not }
 | 
			
		||||
), TokenTree::Prefix));
 | 
			
		||||
 | 
			
		||||
named!(infix(&str) -> TokenTree, map!(alt!(
 | 
			
		||||
    complete!(tag!("==")) => { |_| BinaryOperator::Equ } |
 | 
			
		||||
    complete!(tag!("!=")) => { |_| BinaryOperator::Neq } |
 | 
			
		||||
    complete!(char!('+')) => { |_| BinaryOperator::Add } |
 | 
			
		||||
    complete!(char!('-')) => { |_| BinaryOperator::Sub } |
 | 
			
		||||
    complete!(char!('*')) => { |_| BinaryOperator::Mul } |
 | 
			
		||||
    complete!(char!('/')) => { |_| BinaryOperator::Div } |
 | 
			
		||||
    complete!(char!('^')) => { |_| BinaryOperator::Pow }
 | 
			
		||||
), TokenTree::Infix));
 | 
			
		||||
 | 
			
		||||
named!(primary(&str) -> TokenTree, alt!(
 | 
			
		||||
    do_parse!(
 | 
			
		||||
        multispace0 >>
 | 
			
		||||
        char!('(') >>
 | 
			
		||||
        multispace0 >>
 | 
			
		||||
        group: group >>
 | 
			
		||||
        multispace0 >>
 | 
			
		||||
        char!(')') >>
 | 
			
		||||
        multispace0 >>
 | 
			
		||||
            (TokenTree::Group(group))
 | 
			
		||||
    ) |
 | 
			
		||||
    delimited!(multispace0, simple_expr, multispace0) => { |s| TokenTree::Primary(s) }
 | 
			
		||||
));
 | 
			
		||||
 | 
			
		||||
named!(
 | 
			
		||||
    rest(&str) -> Vec<(TokenTree, Vec<TokenTree>, TokenTree)>,
 | 
			
		||||
    many0!(tuple!(
 | 
			
		||||
        infix,
 | 
			
		||||
        delimited!(multispace0, many0!(prefix), multispace0),
 | 
			
		||||
        primary
 | 
			
		||||
        // many0!(postfix)
 | 
			
		||||
    ))
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
named!(group(&str) -> Vec<TokenTree>, do_parse!(
 | 
			
		||||
    prefix: many0!(prefix)
 | 
			
		||||
        >> primary: primary
 | 
			
		||||
        // >> postfix: many0!(postfix)
 | 
			
		||||
        >> rest: rest
 | 
			
		||||
        >> ({
 | 
			
		||||
            let mut res = prefix;
 | 
			
		||||
            res.push(primary);
 | 
			
		||||
            // res.append(&mut postfix);
 | 
			
		||||
            for (infix, mut prefix, primary/*, mut postfix*/) in rest {
 | 
			
		||||
                res.push(infix);
 | 
			
		||||
                res.append(&mut prefix);
 | 
			
		||||
                res.push(primary);
 | 
			
		||||
                // res.append(&mut postfix);
 | 
			
		||||
            }
 | 
			
		||||
            res
 | 
			
		||||
        })
 | 
			
		||||
));
 | 
			
		||||
 | 
			
		||||
fn token_tree(i: &str) -> nom::IResult<&str, Vec<TokenTree>> {
 | 
			
		||||
    group(i)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct ExprParser;
 | 
			
		||||
 | 
			
		||||
impl<'a, I> PrattParser<I> for ExprParser
 | 
			
		||||
where
 | 
			
		||||
    I: Iterator<Item = TokenTree<'a>>,
 | 
			
		||||
{
 | 
			
		||||
    type Error = pratt::NoError;
 | 
			
		||||
    type Input = TokenTree<'a>;
 | 
			
		||||
    type Output = Expr<'a>;
 | 
			
		||||
 | 
			
		||||
    fn query(&mut self, input: &Self::Input) -> Result<Affix, Self::Error> {
 | 
			
		||||
        use BinaryOperator::*;
 | 
			
		||||
        use UnaryOperator::*;
 | 
			
		||||
 | 
			
		||||
        Ok(match input {
 | 
			
		||||
            TokenTree::Infix(Add) => Affix::Infix(Precedence(6), Associativity::Left),
 | 
			
		||||
            TokenTree::Infix(Sub) => Affix::Infix(Precedence(6), Associativity::Left),
 | 
			
		||||
            TokenTree::Infix(Mul) => Affix::Infix(Precedence(7), Associativity::Left),
 | 
			
		||||
            TokenTree::Infix(Div) => Affix::Infix(Precedence(7), Associativity::Left),
 | 
			
		||||
            TokenTree::Infix(Pow) => Affix::Infix(Precedence(8), Associativity::Right),
 | 
			
		||||
            TokenTree::Infix(Equ) => Affix::Infix(Precedence(4), Associativity::Right),
 | 
			
		||||
            TokenTree::Infix(Neq) => Affix::Infix(Precedence(4), Associativity::Right),
 | 
			
		||||
            TokenTree::Prefix(Neg) => Affix::Prefix(Precedence(6)),
 | 
			
		||||
            TokenTree::Prefix(Not) => Affix::Prefix(Precedence(6)),
 | 
			
		||||
            TokenTree::Primary(_) => Affix::Nilfix,
 | 
			
		||||
            TokenTree::Group(_) => Affix::Nilfix,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn primary(&mut self, input: Self::Input) -> Result<Self::Output, Self::Error> {
 | 
			
		||||
        Ok(match input {
 | 
			
		||||
            TokenTree::Primary(expr) => expr,
 | 
			
		||||
            TokenTree::Group(group) => self.parse(&mut group.into_iter()).unwrap(),
 | 
			
		||||
            _ => unreachable!(),
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn infix(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        lhs: Self::Output,
 | 
			
		||||
        op: Self::Input,
 | 
			
		||||
        rhs: Self::Output,
 | 
			
		||||
    ) -> Result<Self::Output, Self::Error> {
 | 
			
		||||
        let op = match op {
 | 
			
		||||
            TokenTree::Infix(op) => op,
 | 
			
		||||
            _ => unreachable!(),
 | 
			
		||||
        };
 | 
			
		||||
        Ok(Expr::BinaryOp {
 | 
			
		||||
            lhs: Box::new(lhs),
 | 
			
		||||
            op,
 | 
			
		||||
            rhs: Box::new(rhs),
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn prefix(&mut self, op: Self::Input, rhs: Self::Output) -> Result<Self::Output, Self::Error> {
 | 
			
		||||
        let op = match op {
 | 
			
		||||
            TokenTree::Prefix(op) => op,
 | 
			
		||||
            _ => unreachable!(),
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        Ok(Expr::UnaryOp {
 | 
			
		||||
            op,
 | 
			
		||||
            rhs: Box::new(rhs),
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn postfix(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        _lhs: Self::Output,
 | 
			
		||||
        _op: Self::Input,
 | 
			
		||||
    ) -> Result<Self::Output, Self::Error> {
 | 
			
		||||
        unreachable!()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn is_reserved(s: &str) -> bool {
 | 
			
		||||
pub(crate) fn is_reserved(s: &str) -> bool {
 | 
			
		||||
    matches!(s, "if" | "then" | "else" | "let" | "in" | "fn")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn ident<'a, E>(i: &'a str) -> nom::IResult<&'a str, Ident, E>
 | 
			
		||||
pub(crate) fn ident<'a, E>(i: &'a str) -> nom::IResult<&'a str, Ident, E>
 | 
			
		||||
where
 | 
			
		||||
    E: ParseError<&'a str>,
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -188,121 +43,6 @@ where
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
named!(int(&str) -> Literal, map!(flat_map!(digit1, parse_to!(u64)), Literal::Int));
 | 
			
		||||
 | 
			
		||||
named!(literal(&str) -> Expr, map!(alt!(int), Expr::Literal));
 | 
			
		||||
 | 
			
		||||
named!(binding(&str) -> (Ident, Expr), do_parse!(
 | 
			
		||||
    multispace0
 | 
			
		||||
        >> ident: ident
 | 
			
		||||
        >> multispace0
 | 
			
		||||
        >> char!('=')
 | 
			
		||||
        >> multispace0
 | 
			
		||||
        >> expr: expr
 | 
			
		||||
        >> (ident, expr)
 | 
			
		||||
));
 | 
			
		||||
 | 
			
		||||
named!(let_(&str) -> Expr, do_parse!(
 | 
			
		||||
    tag!("let")
 | 
			
		||||
        >> multispace0
 | 
			
		||||
        >> bindings: separated_list1!(alt!(char!(';') | char!('\n')), binding)
 | 
			
		||||
        >> multispace0
 | 
			
		||||
        >> tag!("in")
 | 
			
		||||
        >> multispace0
 | 
			
		||||
        >> body: expr
 | 
			
		||||
        >> (Expr::Let {
 | 
			
		||||
            bindings,
 | 
			
		||||
            body: Box::new(body)
 | 
			
		||||
        })
 | 
			
		||||
));
 | 
			
		||||
 | 
			
		||||
named!(if_(&str) -> Expr, do_parse! (
 | 
			
		||||
    tag!("if")
 | 
			
		||||
        >> multispace0
 | 
			
		||||
        >> condition: expr
 | 
			
		||||
        >> multispace0
 | 
			
		||||
        >> tag!("then")
 | 
			
		||||
        >> multispace0
 | 
			
		||||
        >> then: expr
 | 
			
		||||
        >> multispace0
 | 
			
		||||
        >> tag!("else")
 | 
			
		||||
        >> multispace0
 | 
			
		||||
        >> else_: expr
 | 
			
		||||
        >> (Expr::If {
 | 
			
		||||
            condition: Box::new(condition),
 | 
			
		||||
            then: Box::new(then),
 | 
			
		||||
            else_: Box::new(else_)
 | 
			
		||||
        })
 | 
			
		||||
));
 | 
			
		||||
 | 
			
		||||
named!(ident_expr(&str) -> Expr, map!(ident, Expr::Ident));
 | 
			
		||||
 | 
			
		||||
named!(paren_expr(&str) -> Expr,
 | 
			
		||||
       delimited!(complete!(tag!("(")), expr, complete!(tag!(")"))));
 | 
			
		||||
 | 
			
		||||
named!(funcref(&str) -> Expr, alt!(
 | 
			
		||||
    ident_expr |
 | 
			
		||||
    paren_expr
 | 
			
		||||
));
 | 
			
		||||
 | 
			
		||||
named!(no_arg_call(&str) -> Expr, do_parse!(
 | 
			
		||||
    fun: funcref
 | 
			
		||||
        >> multispace0
 | 
			
		||||
        >> complete!(tag!("()"))
 | 
			
		||||
        >> (Expr::Call {
 | 
			
		||||
            fun: Box::new(fun),
 | 
			
		||||
            args: vec![],
 | 
			
		||||
        })
 | 
			
		||||
));
 | 
			
		||||
 | 
			
		||||
named!(fun_expr(&str) -> Expr, do_parse!(
 | 
			
		||||
    tag!("fn")
 | 
			
		||||
        >> multispace1
 | 
			
		||||
        >> args: separated_list0!(multispace1, ident)
 | 
			
		||||
        >> multispace0
 | 
			
		||||
        >> char!('=')
 | 
			
		||||
        >> multispace0
 | 
			
		||||
        >> body: expr
 | 
			
		||||
        >> (Expr::Fun(Box::new(Fun {
 | 
			
		||||
            args,
 | 
			
		||||
            body
 | 
			
		||||
        })))
 | 
			
		||||
));
 | 
			
		||||
 | 
			
		||||
named!(arg(&str) -> Expr, alt!(
 | 
			
		||||
    ident_expr |
 | 
			
		||||
    literal |
 | 
			
		||||
    paren_expr
 | 
			
		||||
));
 | 
			
		||||
 | 
			
		||||
named!(call_with_args(&str) -> Expr, do_parse!(
 | 
			
		||||
    fun: funcref
 | 
			
		||||
        >> multispace1
 | 
			
		||||
        >> args: separated_list1!(multispace1, arg)
 | 
			
		||||
        >> (Expr::Call {
 | 
			
		||||
            fun: Box::new(fun),
 | 
			
		||||
            args
 | 
			
		||||
        })
 | 
			
		||||
));
 | 
			
		||||
 | 
			
		||||
named!(simple_expr(&str) -> Expr, alt!(
 | 
			
		||||
    let_ |
 | 
			
		||||
    if_ |
 | 
			
		||||
    fun_expr |
 | 
			
		||||
    literal |
 | 
			
		||||
    ident_expr
 | 
			
		||||
));
 | 
			
		||||
 | 
			
		||||
named!(pub expr(&str) -> Expr, alt!(
 | 
			
		||||
    no_arg_call |
 | 
			
		||||
    call_with_args |
 | 
			
		||||
    map!(token_tree, |tt| {
 | 
			
		||||
        ExprParser.parse(&mut tt.into_iter()).unwrap()
 | 
			
		||||
    }) |
 | 
			
		||||
    simple_expr));
 | 
			
		||||
 | 
			
		||||
//////
 | 
			
		||||
 | 
			
		||||
named!(fun_decl(&str) -> Decl, do_parse!(
 | 
			
		||||
    complete!(tag!("fn"))
 | 
			
		||||
        >> multispace0
 | 
			
		||||
| 
						 | 
				
			
			@ -330,170 +70,10 @@ named!(pub toplevel(&str) -> Vec<Decl>, many0!(decl));
 | 
			
		|||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use nom_trace::print_trace;
 | 
			
		||||
    use std::convert::{TryFrom, TryInto};
 | 
			
		||||
    use std::convert::TryInto;
 | 
			
		||||
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use BinaryOperator::*;
 | 
			
		||||
    use Expr::{BinaryOp, If, Let, UnaryOp};
 | 
			
		||||
    use UnaryOperator::*;
 | 
			
		||||
 | 
			
		||||
    fn ident_expr(s: &str) -> Box<Expr> {
 | 
			
		||||
        Box::new(Expr::Ident(Ident::try_from(s).unwrap()))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    macro_rules! test_parse {
 | 
			
		||||
        ($parser: ident, $src: expr) => {{
 | 
			
		||||
            let res = $parser($src);
 | 
			
		||||
            print_trace!();
 | 
			
		||||
            let (rem, res) = res.unwrap();
 | 
			
		||||
            assert!(
 | 
			
		||||
                rem.is_empty(),
 | 
			
		||||
                "non-empty remainder: \"{}\", parsed: {:?}",
 | 
			
		||||
                rem,
 | 
			
		||||
                res
 | 
			
		||||
            );
 | 
			
		||||
            res
 | 
			
		||||
        }};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    mod operators {
 | 
			
		||||
        use super::*;
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn mul_plus() {
 | 
			
		||||
            let (rem, res) = expr("x*y+z").unwrap();
 | 
			
		||||
            assert!(rem.is_empty());
 | 
			
		||||
            assert_eq!(
 | 
			
		||||
                res,
 | 
			
		||||
                BinaryOp {
 | 
			
		||||
                    lhs: Box::new(BinaryOp {
 | 
			
		||||
                        lhs: ident_expr("x"),
 | 
			
		||||
                        op: Mul,
 | 
			
		||||
                        rhs: ident_expr("y")
 | 
			
		||||
                    }),
 | 
			
		||||
                    op: Add,
 | 
			
		||||
                    rhs: ident_expr("z")
 | 
			
		||||
                }
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn mul_plus_ws() {
 | 
			
		||||
            let (rem, res) = expr("x * y    +    z").unwrap();
 | 
			
		||||
            assert!(rem.is_empty(), "non-empty remainder: \"{}\"", rem);
 | 
			
		||||
            assert_eq!(
 | 
			
		||||
                res,
 | 
			
		||||
                BinaryOp {
 | 
			
		||||
                    lhs: Box::new(BinaryOp {
 | 
			
		||||
                        lhs: ident_expr("x"),
 | 
			
		||||
                        op: Mul,
 | 
			
		||||
                        rhs: ident_expr("y")
 | 
			
		||||
                    }),
 | 
			
		||||
                    op: Add,
 | 
			
		||||
                    rhs: ident_expr("z")
 | 
			
		||||
                }
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn unary() {
 | 
			
		||||
            let (rem, res) = expr("x * -z").unwrap();
 | 
			
		||||
            assert!(rem.is_empty(), "non-empty remainder: \"{}\"", rem);
 | 
			
		||||
            assert_eq!(
 | 
			
		||||
                res,
 | 
			
		||||
                BinaryOp {
 | 
			
		||||
                    lhs: ident_expr("x"),
 | 
			
		||||
                    op: Mul,
 | 
			
		||||
                    rhs: Box::new(UnaryOp {
 | 
			
		||||
                        op: Neg,
 | 
			
		||||
                        rhs: ident_expr("z"),
 | 
			
		||||
                    })
 | 
			
		||||
                }
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn mul_literal() {
 | 
			
		||||
            let (rem, res) = expr("x * 3").unwrap();
 | 
			
		||||
            assert!(rem.is_empty());
 | 
			
		||||
            assert_eq!(
 | 
			
		||||
                res,
 | 
			
		||||
                BinaryOp {
 | 
			
		||||
                    lhs: ident_expr("x"),
 | 
			
		||||
                    op: Mul,
 | 
			
		||||
                    rhs: Box::new(Expr::Literal(Literal::Int(3))),
 | 
			
		||||
                }
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn equ() {
 | 
			
		||||
            let res = test_parse!(expr, "x * 7 == 7");
 | 
			
		||||
            assert_eq!(
 | 
			
		||||
                res,
 | 
			
		||||
                BinaryOp {
 | 
			
		||||
                    lhs: Box::new(BinaryOp {
 | 
			
		||||
                        lhs: ident_expr("x"),
 | 
			
		||||
                        op: Mul,
 | 
			
		||||
                        rhs: Box::new(Expr::Literal(Literal::Int(7)))
 | 
			
		||||
                    }),
 | 
			
		||||
                    op: Equ,
 | 
			
		||||
                    rhs: Box::new(Expr::Literal(Literal::Int(7)))
 | 
			
		||||
                }
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn let_complex() {
 | 
			
		||||
        let res = test_parse!(expr, "let x = 1; y = x * 7 in (x + y) * 4");
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            res,
 | 
			
		||||
            Let {
 | 
			
		||||
                bindings: vec![
 | 
			
		||||
                    (
 | 
			
		||||
                        Ident::try_from("x").unwrap(),
 | 
			
		||||
                        Expr::Literal(Literal::Int(1))
 | 
			
		||||
                    ),
 | 
			
		||||
                    (
 | 
			
		||||
                        Ident::try_from("y").unwrap(),
 | 
			
		||||
                        Expr::BinaryOp {
 | 
			
		||||
                            lhs: ident_expr("x"),
 | 
			
		||||
                            op: Mul,
 | 
			
		||||
                            rhs: Box::new(Expr::Literal(Literal::Int(7)))
 | 
			
		||||
                        }
 | 
			
		||||
                    )
 | 
			
		||||
                ],
 | 
			
		||||
                body: Box::new(Expr::BinaryOp {
 | 
			
		||||
                    lhs: Box::new(Expr::BinaryOp {
 | 
			
		||||
                        lhs: ident_expr("x"),
 | 
			
		||||
                        op: Add,
 | 
			
		||||
                        rhs: ident_expr("y"),
 | 
			
		||||
                    }),
 | 
			
		||||
                    op: Mul,
 | 
			
		||||
                    rhs: Box::new(Expr::Literal(Literal::Int(4))),
 | 
			
		||||
                })
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn if_simple() {
 | 
			
		||||
        let res = test_parse!(expr, "if x == 8 then 9 else 20");
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            res,
 | 
			
		||||
            If {
 | 
			
		||||
                condition: Box::new(BinaryOp {
 | 
			
		||||
                    lhs: ident_expr("x"),
 | 
			
		||||
                    op: Equ,
 | 
			
		||||
                    rhs: Box::new(Expr::Literal(Literal::Int(8))),
 | 
			
		||||
                }),
 | 
			
		||||
                then: Box::new(Expr::Literal(Literal::Int(9))),
 | 
			
		||||
                else_: Box::new(Expr::Literal(Literal::Int(20)))
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
    use expr::tests::ident_expr;
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn fn_decl() {
 | 
			
		||||
| 
						 | 
				
			
			@ -510,69 +90,6 @@ mod tests {
 | 
			
		|||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn no_arg_call() {
 | 
			
		||||
        let res = test_parse!(expr, "f()");
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            res,
 | 
			
		||||
            Expr::Call {
 | 
			
		||||
                fun: ident_expr("f"),
 | 
			
		||||
                args: vec![]
 | 
			
		||||
            }
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn call_with_args() {
 | 
			
		||||
        let res = test_parse!(expr, "f x 1");
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            res,
 | 
			
		||||
            Expr::Call {
 | 
			
		||||
                fun: ident_expr("f"),
 | 
			
		||||
                args: vec![*ident_expr("x"), Expr::Literal(Literal::Int(1))]
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn call_funcref() {
 | 
			
		||||
        let res = test_parse!(expr, "(let x = 1 in x) 2");
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            res,
 | 
			
		||||
            Expr::Call {
 | 
			
		||||
                fun: Box::new(Expr::Let {
 | 
			
		||||
                    bindings: vec![(
 | 
			
		||||
                        Ident::try_from("x").unwrap(),
 | 
			
		||||
                        Expr::Literal(Literal::Int(1))
 | 
			
		||||
                    )],
 | 
			
		||||
                    body: ident_expr("x")
 | 
			
		||||
                }),
 | 
			
		||||
                args: vec![Expr::Literal(Literal::Int(2))]
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn anon_function() {
 | 
			
		||||
        let res = test_parse!(expr, "let id = fn x = x in id 1");
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            res,
 | 
			
		||||
            Expr::Let {
 | 
			
		||||
                bindings: vec![(
 | 
			
		||||
                    Ident::try_from("id").unwrap(),
 | 
			
		||||
                    Expr::Fun(Box::new(Fun {
 | 
			
		||||
                        args: vec![Ident::try_from("x").unwrap()],
 | 
			
		||||
                        body: *ident_expr("x")
 | 
			
		||||
                    }))
 | 
			
		||||
                )],
 | 
			
		||||
                body: Box::new(Expr::Call {
 | 
			
		||||
                    fun: ident_expr("id"),
 | 
			
		||||
                    args: vec![Expr::Literal(Literal::Int(1))],
 | 
			
		||||
                })
 | 
			
		||||
            }
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn multiple_decls() {
 | 
			
		||||
        let res = test_parse!(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue