diff --git a/yurtc/src/ast.rs b/yurtc/src/ast.rs index 7f9d2432d..23e89f4da 100644 --- a/yurtc/src/ast.rs +++ b/yurtc/src/ast.rs @@ -76,6 +76,7 @@ pub(super) enum Expr { tuple: Box, field: Either, }, + Parens(Box), } #[derive(Clone, Debug, PartialEq)] diff --git a/yurtc/src/parser.rs b/yurtc/src/parser.rs index 9aa04c3c8..04ee70acb 100644 --- a/yurtc/src/parser.rs +++ b/yurtc/src/parser.rs @@ -295,6 +295,11 @@ fn expr<'sc>() -> impl Parser, ast::Expr, Error = ParseError<'sc>> + }) .map(ast::Expr::Tuple); + let parens = expr + .clone() + .delimited_by(just(Token::ParenOpen), just(Token::ParenClose)) + .map(|expr| ast::Expr::Parens(Box::new(expr))); + let atom = choice(( immediate().map(ast::Expr::Immediate), unary_op(expr.clone()), @@ -303,6 +308,7 @@ fn expr<'sc>() -> impl Parser, ast::Expr, Error = ParseError<'sc>> + cond_expr(expr.clone()), call, tuple, + parens, ident().map(ast::Expr::Ident), )); diff --git a/yurtc/src/parser/tests.rs b/yurtc/src/parser/tests.rs index c0fbbc78c..a0aa864fd 100644 --- a/yurtc/src/parser/tests.rs +++ b/yurtc/src/parser/tests.rs @@ -561,6 +561,98 @@ fn complex_exprs() { ); } +#[test] +fn parens_exprs() { + check( + &run_parser!(expr(), "(1 + 2) * 3"), + expect_test::expect!["BinaryOp { op: Mul, lhs: Parens(BinaryOp { op: Add, lhs: Immediate(Int(1)), rhs: Immediate(Int(2)) }), rhs: Immediate(Int(3)) }"], + ); + check( + &run_parser!(expr(), "1 * (2 + 3)"), + expect_test::expect!["BinaryOp { op: Mul, lhs: Immediate(Int(1)), rhs: Parens(BinaryOp { op: Add, lhs: Immediate(Int(2)), rhs: Immediate(Int(3)) }) }"], + ); + check( + &run_parser!(expr(), "(1 + 2) * (3 + 4)"), + expect_test::expect!["BinaryOp { op: Mul, lhs: Parens(BinaryOp { op: Add, lhs: Immediate(Int(1)), rhs: Immediate(Int(2)) }), rhs: Parens(BinaryOp { op: Add, lhs: Immediate(Int(3)), rhs: Immediate(Int(4)) }) }"], + ); + check( + &run_parser!(expr(), "(1 + (2 * 3)) * 4"), + expect_test::expect!["BinaryOp { op: Mul, lhs: Parens(BinaryOp { op: Add, lhs: Immediate(Int(1)), rhs: Parens(BinaryOp { op: Mul, lhs: Immediate(Int(2)), rhs: Immediate(Int(3)) }) }), rhs: Immediate(Int(4)) }"], + ); + check( + &run_parser!(expr(), "(1 * (2 + 3)) * 4"), + expect_test::expect!["BinaryOp { op: Mul, lhs: Parens(BinaryOp { op: Mul, lhs: Immediate(Int(1)), rhs: Parens(BinaryOp { op: Add, lhs: Immediate(Int(2)), rhs: Immediate(Int(3)) }) }), rhs: Immediate(Int(4)) }"], + ); + check( + &run_parser!(expr(), "((1 + 2) * 3) * 4"), + expect_test::expect!["BinaryOp { op: Mul, lhs: Parens(BinaryOp { op: Mul, lhs: Parens(BinaryOp { op: Add, lhs: Immediate(Int(1)), rhs: Immediate(Int(2)) }), rhs: Immediate(Int(3)) }), rhs: Immediate(Int(4)) }"], + ); + check( + &run_parser!(expr(), "((1 + 2) * (3 + 4)) * 5"), + expect_test::expect!["BinaryOp { op: Mul, lhs: Parens(BinaryOp { op: Mul, lhs: Parens(BinaryOp { op: Add, lhs: Immediate(Int(1)), rhs: Immediate(Int(2)) }), rhs: Parens(BinaryOp { op: Add, lhs: Immediate(Int(3)), rhs: Immediate(Int(4)) }) }), rhs: Immediate(Int(5)) }"], + ); + check( + &run_parser!(expr(), "(1 + 2) * 3 / 4"), + expect_test::expect!["BinaryOp { op: Div, lhs: BinaryOp { op: Mul, lhs: Parens(BinaryOp { op: Add, lhs: Immediate(Int(1)), rhs: Immediate(Int(2)) }), rhs: Immediate(Int(3)) }, rhs: Immediate(Int(4)) }"], + ); + check( + &run_parser!(expr(), "1 / (2 + 3) * 4"), + expect_test::expect!["BinaryOp { op: Mul, lhs: BinaryOp { op: Div, lhs: Immediate(Int(1)), rhs: Parens(BinaryOp { op: Add, lhs: Immediate(Int(2)), rhs: Immediate(Int(3)) }) }, rhs: Immediate(Int(4)) }"], + ); + check( + &run_parser!(expr(), "(1 < 2) && (3 > 4)"), + expect_test::expect!["BinaryOp { op: LogicalAnd, lhs: Parens(BinaryOp { op: LessThan, lhs: Immediate(Int(1)), rhs: Immediate(Int(2)) }), rhs: Parens(BinaryOp { op: GreaterThan, lhs: Immediate(Int(3)), rhs: Immediate(Int(4)) }) }"], + ); + check( + &run_parser!(expr(), "(1 == 2) || (3 != 4)"), + expect_test::expect!["BinaryOp { op: LogicalOr, lhs: Parens(BinaryOp { op: Equal, lhs: Immediate(Int(1)), rhs: Immediate(Int(2)) }), rhs: Parens(BinaryOp { op: NotEqual, lhs: Immediate(Int(3)), rhs: Immediate(Int(4)) }) }"], + ); + check( + &run_parser!(expr(), "1 < (2 && 3) > 4"), + expect_test::expect!["BinaryOp { op: GreaterThan, lhs: BinaryOp { op: LessThan, lhs: Immediate(Int(1)), rhs: Parens(BinaryOp { op: LogicalAnd, lhs: Immediate(Int(2)), rhs: Immediate(Int(3)) }) }, rhs: Immediate(Int(4)) }"], + ); + check( + &run_parser!(expr(), "1 && (2 || 3)"), + expect_test::expect!["BinaryOp { op: LogicalAnd, lhs: Immediate(Int(1)), rhs: Parens(BinaryOp { op: LogicalOr, lhs: Immediate(Int(2)), rhs: Immediate(Int(3)) }) }"], + ); + check( + &run_parser!(expr(), "1 == (2 || 3) != 4"), + expect_test::expect!["BinaryOp { op: NotEqual, lhs: BinaryOp { op: Equal, lhs: Immediate(Int(1)), rhs: Parens(BinaryOp { op: LogicalOr, lhs: Immediate(Int(2)), rhs: Immediate(Int(3)) }) }, rhs: Immediate(Int(4)) }"], + ); + check( + &run_parser!(expr(), "-(1 + 2)"), + expect_test::expect!["UnaryOp { op: Neg, expr: Parens(BinaryOp { op: Add, lhs: Immediate(Int(1)), rhs: Immediate(Int(2)) }) }"], + ); + check( + &run_parser!(expr(), "!(a < b)"), + expect_test::expect!["UnaryOp { op: Not, expr: Parens(BinaryOp { op: LessThan, lhs: Ident(Ident(\"a\")), rhs: Ident(Ident(\"b\")) }) }"], + ); + check( + &run_parser!(expr(), "(1)"), + expect_test::expect!["Parens(Immediate(Int(1)))"], + ); + check( + &run_parser!(expr(), "(a)"), + expect_test::expect!["Parens(Ident(Ident(\"a\")))"], + ); + check( + &run_parser!(expr(), "()"), + expect_test::expect![[r#" + @1..2: found ")" but expected "!", "+", "-", "{", "{", "(", "if", or "cond" + "#]], + ); + check( + &run_parser!(expr(), "(if a < b { 1 } else { 2 })"), + expect_test::expect![[r#" + Parens(If(IfExpr { condition: BinaryOp { op: LessThan, lhs: Ident(Ident("a")), rhs: Ident(Ident("b")) }, then_block: Block { statements: [], final_expr: Immediate(Int(1)) }, else_block: Block { statements: [], final_expr: Immediate(Int(2)) } }))"#]], + ); + check( + &run_parser!(expr(), "(foo(a, b, c))"), + expect_test::expect![[r#" + Parens(Call { name: Ident("foo"), args: [Ident(Ident("a")), Ident(Ident("b")), Ident(Ident("c"))] })"#]], + ); +} + #[test] fn idents() { check( @@ -964,7 +1056,7 @@ fn cond_exprs() { check( &run_parser!(cond_expr(expr()), r#"cond { a => b, }"#), expect_test::expect![[r#" - @15..16: found "}" but expected "!", "+", "-", "{", "{", "if", "else", or "cond" + @15..16: found "}" but expected "!", "+", "-", "{", "{", "(", "if", "else", or "cond" "#]], ); @@ -1019,7 +1111,7 @@ fn fn_errors() { check( &run_parser!(yurt_program(), "fn foo() -> real {}"), expect_test::expect![[r#" - @18..19: found "}" but expected "!", "+", "-", "{", "{", "if", "cond", "let", or "constraint" + @18..19: found "}" but expected "!", "+", "-", "{", "{", "(", "if", "cond", "let", or "constraint" "#]], ); }