diff options
author | Griffin Smith <root@gws.fyi> | 2021-03-13T17·19-0500 |
---|---|---|
committer | Griffin Smith <root@gws.fyi> | 2021-03-13T17·19-0500 |
commit | 3dff189499af1ddd60d8fc128b794d15f1cb19ae (patch) | |
tree | 7349a51c332fd2049c1b7874eff434fe9e85a6e5 /src | |
parent | 1ea2d8ba9fef3bee2c757d19d8b42e541e3fe390 (diff) |
Factor out expr parser into its own module
Diffstat (limited to 'src')
-rw-r--r-- | src/main.rs | 1 | ||||
-rw-r--r-- | src/parser/expr.rs | 482 | ||||
-rw-r--r-- | src/parser/macros.rs | 15 | ||||
-rw-r--r-- | src/parser/mod.rs | 507 |
4 files changed, 510 insertions, 495 deletions
diff --git a/src/main.rs b/src/main.rs index c31fda32dbc4..b539ebbb3d99 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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}; diff --git a/src/parser/expr.rs b/src/parser/expr.rs new file mode 100644 index 000000000000..a42c7a6c765e --- /dev/null +++ b/src/parser/expr.rs @@ -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))], + }) + } + ); + } +} diff --git a/src/parser/macros.rs b/src/parser/macros.rs new file mode 100644 index 000000000000..60db5133dc0f --- /dev/null +++ b/src/parser/macros.rs @@ -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 + }}; +} diff --git a/src/parser/mod.rs b/src/parser/mod.rs index be432b8adf56..3e162d449320 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -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; -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::*; +use crate::ast::{Decl, Fun, Ident}; +pub use expr::expr; - 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!() - } -} +pub type Error = nom::Err<nom::error::Error<String>>; -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() { @@ -511,69 +91,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!( toplevel, |