diff --git a/schala-lang/language/src/parsing/mod.rs b/schala-lang/language/src/parsing/mod.rs index 9636c37..afe156c 100644 --- a/schala-lang/language/src/parsing/mod.rs +++ b/schala-lang/language/src/parsing/mod.rs @@ -166,7 +166,6 @@ //! //! module := 'module' IDENTIFIER '{' statement* '}' //! ``` -mod new_tests; mod test; use std::rc::Rc; diff --git a/schala-lang/language/src/parsing/new_tests.rs b/schala-lang/language/src/parsing/new_tests.rs deleted file mode 100644 index 1ea2899..0000000 --- a/schala-lang/language/src/parsing/new_tests.rs +++ /dev/null @@ -1,1207 +0,0 @@ -#![cfg(test)] -#![allow(clippy::upper_case_acronyms)] -#![allow(clippy::vec_init_then_push)] -//use test_case::test_case; -use std::rc::Rc; - -use pretty_assertions::assert_eq; - -use super::{tokenize, ParseResult, Parser}; -use crate::{ast::*, tokenizing::Location}; - -fn rc(s: &str) -> Rc { - Rc::new(s.to_owned()) -} - -fn bx(item: T) -> Box { - Box::new(item) -} - -fn make_parser(input: &str) -> Parser { - let tokens: Vec = tokenize(input); - let mut parser = super::Parser::new(); - parser.add_new_tokens(tokens); - parser -} - -fn parse(input: &str) -> ParseResult { - let mut parser = make_parser(input); - parser.parse() -} - -fn stmt(kind: StatementKind) -> Statement { - Statement { location: Location::default(), id: ItemId::default(), kind } -} - -fn exst(kind: ExpressionKind) -> Statement { - Statement { - location: Location::default(), - id: ItemId::default(), - kind: StatementKind::Expression(expr(kind)), - } -} - -fn decl(declaration: Declaration) -> Statement { - Statement { - location: Location::default(), - id: ItemId::default(), - kind: StatementKind::Declaration(declaration), - } -} - -fn fn_decl(sig: Signature, stmts: Block) -> Statement { - Statement { - kind: StatementKind::Declaration(Declaration::FuncDecl(sig, stmts)), - location: Default::default(), - id: Default::default(), - } -} - -fn expr_anno(kind: ExpressionKind, anno: TypeIdentifier) -> Expression { - Expression { id: ItemId::default(), kind, type_anno: Some(anno) } -} - -fn expr(kind: ExpressionKind) -> Expression { - Expression { id: ItemId::default(), kind, type_anno: None } -} - -fn binop(sigil: &str, lhs: Expression, rhs: Expression) -> Expression { - Expression { - id: Default::default(), - type_anno: None, - kind: ExpressionKind::BinExp(BinOp::from_sigil(sigil), Box::new(lhs), Box::new(rhs)), - } -} - -fn prefixop(sigil: &str, exp: Expression) -> Expression { - Expression { - id: Default::default(), - type_anno: None, - kind: ExpressionKind::PrefixExp(PrefixOp::from_sigil(sigil), Box::new(exp)), - } -} - -macro_rules! qn { - ( $( $component:ident),* ) => { - { - let mut components = vec![]; - $( - components.push(rc(stringify!($component))); - )* - QualifiedName { components, id: Default::default() } - } - }; -} - -fn ty_simple(name: &str) -> TypeIdentifier { - TypeIdentifier::Singleton(TypeSingletonName { name: rc(name), params: vec![] }) -} - -macro_rules! assert_ast { - ($input:expr, $statements:expr) => { - let ast = parse($input).unwrap(); - let expected = AST { id: Default::default(), statements: $statements.into() }; - println!("Expected: {}", expected); - println!("Actual: {}", ast); - assert_eq!(ast, expected); - }; -} - -macro_rules! assert_fail { - ($input:expr, $failure:expr) => { - let err = parse($input).unwrap_err(); - assert_eq!(err.msg, $failure); - }; -} - -macro_rules! assert_expr { - ($input:expr, $correct:expr) => { - let mut parser = make_parser($input); - assert_eq!(parser.expression().unwrap(), $correct); - }; -} - -macro_rules! assert_fail_expr { - ($input:expr, $failure:expr) => { - let mut parser = make_parser($input); - let err = parser.expression().unwrap_err(); - assert_eq!(err.msg, $failure); - }; -} -#[test] -fn basic_literals() { - use ExpressionKind::*; - - assert_expr!(".2", expr(FloatLiteral(0.2))); - assert_expr!("8.1", expr(FloatLiteral(8.1))); - assert_expr!("0b010", expr(NatLiteral(2))); - assert_expr!("0b0_1_0", expr(NatLiteral(2))); - assert_expr!("0xff", expr(NatLiteral(255))); - assert_expr!("0x032f", expr(NatLiteral(815))); - assert_expr!("0xf_f_", expr(NatLiteral(255))); - assert_expr!("false", expr(BoolLiteral(false))); - assert_expr!("true", expr(BoolLiteral(true))); - assert_expr!(r#""hello""#, expr(StringLiteral(rc("hello")))); -} - -#[test] -fn list_literals() { - use ExpressionKind::*; - - assert_expr!("[]", expr(ListLiteral(vec![]))); - assert_expr!("[1,2]", expr(ListLiteral(vec![expr(NatLiteral(1)), expr(NatLiteral(2)),]))); -} - -#[test] -fn binexps() { - use ExpressionKind::*; - use StatementKind::Expression; - - assert_expr!("0xf_f_+1", binop("+", expr(NatLiteral(255)), expr(NatLiteral(1)))); - assert_eq!( - parse("3; 4; 4.3").unwrap(), - AST { - id: Default::default(), - statements: vec![ - stmt(Expression(expr(NatLiteral(3)))), - stmt(Expression(expr(NatLiteral(4)))), - stmt(Expression(expr(FloatLiteral(4.3)))), - ] - .into() - } - ); - - assert_expr!( - "1 + 2 * 3", - binop("+", expr(NatLiteral(1)), binop("*", expr(NatLiteral(2)), expr(NatLiteral(3)))) - ); - assert_expr!( - "1 * 2 + 3", - binop("+", binop("*", expr(NatLiteral(1)), expr(NatLiteral(2))), expr(NatLiteral(3))) - ); - assert_expr!("1 && 2", binop("&&", expr(NatLiteral(1)), expr(NatLiteral(2)))); - assert_expr!( - "1 + 2 * 3 + 4", - binop( - "+", - binop("+", expr(NatLiteral(1)), binop("*", expr(NatLiteral(2)), expr(NatLiteral(3)))), - expr(NatLiteral(4)) - ) - ); - assert_expr!( - "(1 + 2) * 3", - binop("*", binop("+", expr(NatLiteral(1)), expr(NatLiteral(2))), expr(NatLiteral(3))) - ); - assert_expr!(".1 + .2", binop("+", expr(FloatLiteral(0.1)), expr(FloatLiteral(0.2)))); - assert_expr!("1 / 2.", binop("/", expr(NatLiteral(1)), expr(FloatLiteral(2.)))); -} - -#[test] -fn prefix_exps() { - use ExpressionKind::*; - - assert_expr!("-3", prefixop("-", expr(NatLiteral(3)))); - assert_expr!("-0.2", prefixop("-", expr(FloatLiteral(0.2)))); - assert_expr!("!3", prefixop("!", expr(NatLiteral(3)))); - assert_expr!("!t", prefixop("!", expr(Value(qn!(t))))); - assert_expr!("a <- -b", binop("<-", expr(Value(qn!(a))), prefixop("-", expr(Value(qn!(b)))))); - assert_expr!("a <--b", binop("<--", expr(Value(qn!(a))), expr(Value(qn!(b))))); -} - -#[test] -fn operators() { - use ExpressionKind::*; - - assert_expr!("a <- 1", binop("<-", expr(Value(qn!(a))), expr(NatLiteral(1)))); - assert_expr!("a || 1", binop("||", expr(Value(qn!(a))), expr(NatLiteral(1)))); - assert_expr!("a <> 1", binop("<>", expr(Value(qn!(a))), expr(NatLiteral(1)))); -} - -#[test] -fn accessors() { - use ExpressionKind::*; - - assert_expr!("a.b", expr(Access { name: rc("b"), expr: bx(expr(Value(qn!(a)))) })); - assert_expr!( - "a.b.c", - expr(Access { - name: rc("c"), - expr: bx(expr(Access { name: rc("b"), expr: bx(expr(Value(qn!(a)))) })) - }) - ); - assert_expr!( - "a.b.c(3)", - expr(Call { - f: bx(expr(Access { - name: rc("c"), - expr: bx(expr(Access { name: rc("b"), expr: bx(expr(Value(qn!(a)))) })) - })), - arguments: vec![InvocationArgument::Positional(expr(NatLiteral(3)))], - }) - ); - assert_expr!( - "a.b().c", - expr(Access { - name: rc("c"), - expr: bx(expr(Call { - f: bx(expr(Access { name: rc("b"), expr: bx(expr(Value(qn!(a)))) })), - arguments: vec![] - })) - }) - ); -} - -#[test] -fn tuples() { - use ExpressionKind::*; - - assert_expr!("()", expr(TupleLiteral(vec![]))); - assert_expr!( - r#"("hella", 34)"#, - expr(TupleLiteral(vec![expr(StringLiteral(rc("hella"))), expr(NatLiteral(34))])) - ); - assert_expr!( - r#"(1+2, "slough")"#, - expr(TupleLiteral(vec![ - binop("+", expr(NatLiteral(1)), expr(NatLiteral(2))), - expr(StringLiteral(rc("slough"))), - ])) - ); -} - -#[test] -fn identifiers() { - use ExpressionKind::*; - - assert_expr!("a", expr(Value(qn!(a)))); - assert_expr!("some_value", expr(Value(qn!(some_value)))); - assert_expr!("alpha::beta::gamma", expr(Value(qn!(alpha, beta, gamma)))); - assert_expr!("a + b", binop("+", expr(Value(qn!(a))), expr(Value(qn!(b))))); - assert_expr!("None", expr(Value(qn!(None)))); - assert_expr!( - "thing::item::call()", - expr(Call { f: bx(expr(Value(qn!(thing, item, call)))), arguments: vec![] }) - ); -} - -#[test] -fn named_struct() { - use ExpressionKind::*; - assert_expr!( - "Pandas { a: x + y }", - expr(NamedStruct { - name: qn!(Pandas), - fields: vec![(rc("a"), binop("+", expr(Value(qn!(x))), expr(Value(qn!(y)))))] - }) - ); - assert_expr!( - "Trousers { a:1, b:800 }", - expr(NamedStruct { - name: qn!(Trousers), - fields: vec![(rc("a"), expr(NatLiteral(1))), (rc("b"), expr(NatLiteral(800)))] - }) - ); -} - -#[test] -fn index() { - use ExpressionKind::*; - assert_expr!( - "a[b,c]", - expr(Index { - indexee: bx(expr(Value(qn!(a)))), - indexers: vec![expr(Value(qn!(b))), expr(Value(qn!(c)))] - }) - ); - assert_expr!( - "perspicacity()[a]", - expr(Index { - indexee: bx(expr(Call { f: bx(expr(Value(qn!(perspicacity)))), arguments: vec![] })), - indexers: vec![expr(Value(qn!(a)))] - }) - ); - - let a = expr(Call { f: bx(expr(Value(qn!(a)))), arguments: vec![] }); - let b = expr(Index { indexee: bx(a), indexers: vec![expr(Value(qn!(b)))] }); - let c = expr(Call { f: bx(b), arguments: vec![] }); - let d = expr(Index { indexee: bx(c), indexers: vec![expr(Value(qn!(d)))] }); - assert_expr!("a()[b]()[d]", d); - - assert_fail_expr!("a[]", "Empty index expressions are not allowed"); -} - -#[test] -fn while_expression() { - use ExpressionKind::*; - - assert_expr!("while { }", expr(WhileExpression { condition: None, body: Block::default() })); - assert_expr!( - "while a == b { }", - expr(WhileExpression { - condition: Some(bx(binop("==", expr(Value(qn!(a))), expr(Value(qn!(b)))))), - body: Block::default() - }) - ); -} - -#[test] -fn for_expression() { - use ExpressionKind::*; - - assert_expr!( - "for { a <- garodzny::maybeValue } return 1", - expr(ForExpression { - enumerators: vec![Enumerator { id: rc("a"), generator: expr(Value(qn!(garodzny, maybeValue))) }], - body: bx(ForBody::MonadicReturn(expr(NatLiteral(1)))) - }) - ); - - assert_expr!( - "for n <- someRange { f(n); }", - expr(ForExpression { - enumerators: vec![Enumerator { id: rc("n"), generator: expr(Value(qn!(someRange))) }], - body: bx(ForBody::StatementBlock( - vec![stmt(StatementKind::Expression(expr(Call { - f: bx(expr(Value(qn!(f)))), - arguments: vec![InvocationArgument::Positional(expr(Value(qn!(n))))], - }))),] - .into() - )), - }) - ); -} - -#[test] -fn lambda_expressions() { - use ExpressionKind::*; - - assert_expr!( - r#"\(x) { x + 1}"#, - expr(Lambda { - params: vec![FormalParam { name: rc!(x), anno: None, default: None }], - type_anno: None, - body: - vec![stmt(StatementKind::Expression(binop("+", expr(Value(qn!(x))), expr(NatLiteral(1))))),] - .into() - }) - ); - - assert_expr!( - r#"\ (x: Int, y) { a;b;c;}"#, - expr(Lambda { - params: vec![ - FormalParam { name: rc!(x), anno: Some(ty_simple("Int")), default: None }, - FormalParam { name: rc!(y), anno: None, default: None }, - ], - type_anno: None, - body: vec![ - stmt(StatementKind::Expression(expr(Value(qn!(a))))), - stmt(StatementKind::Expression(expr(Value(qn!(b))))), - stmt(StatementKind::Expression(expr(Value(qn!(c))))), - ] - .into() - }) - ); - - assert_expr!( - r#"\(x){y}(1)"#, - expr(Call { - f: bx(expr(Lambda { - params: vec![FormalParam { name: rc!(x), anno: None, default: None },], - type_anno: None, - body: vec![stmt(StatementKind::Expression(expr(Value(qn!(y))))),].into() - })), - arguments: vec![InvocationArgument::Positional(expr(NatLiteral(1)))], - }) - ); - - assert_expr!( - r#"\(x: Int): String { "q" }"#, - expr(Lambda { - params: vec![FormalParam { name: rc!(x), anno: Some(ty_simple("Int")), default: None },], - type_anno: Some(TypeIdentifier::Singleton(TypeSingletonName { - name: rc("String"), - params: vec![] - })), - body: vec![stmt(StatementKind::Expression(expr(StringLiteral(rc("q"))))),].into() - }) - ); -} - -#[test] -fn single_param_lambda() { - use ExpressionKind::*; - - assert_expr!( - r#"\x { x + 10 }"#, - expr(Lambda { - params: vec![FormalParam { name: rc!(x), anno: None, default: None },], - type_anno: None, - body: vec![stmt(StatementKind::Expression(binop( - "+", - expr(Value(qn!(x))), - expr(NatLiteral(10)) - )))] - .into() - }) - ); - - assert_expr!( - r#"\x: Int { x + 10 }"#, - expr(Lambda { - params: vec![FormalParam { name: rc!(x), anno: Some(ty_simple("Int")), default: None },], - type_anno: None, - body: vec![stmt(StatementKind::Expression(binop( - "+", - expr(Value(qn!(x))), - expr(NatLiteral(10)) - )))] - .into() - }) - ); -} - -#[test] -fn complex_lambdas() { - use ExpressionKind::*; - - assert_ast! { - r#"fn wahoo() { let a = 10; \(x) { x + a } }; - wahoo()(3) "#, - vec![ - fn_decl(Signature { name: rc("wahoo"), operator: false, type_anno: None, params: vec![] }, - vec![ - decl(Declaration::Binding { - name: rc("a"), - constant: true, - type_anno: None, - expr: expr(NatLiteral(10)) - }), - stmt(StatementKind::Expression(expr(Lambda { - params: vec![ - FormalParam { name: rc("x"), default: None, anno: None } - ], - type_anno: None, - body: vec![ - stmt(StatementKind::Expression(binop("+", expr(Value(qn!(x))), expr(Value(qn!(a)))))), - ].into() - }))), - ].into()), - stmt(StatementKind::Expression(expr(Call { - f: bx(expr(Call { - f: bx(expr(Value(qn!(wahoo)))), - arguments: vec![] })), - arguments: vec![ - InvocationArgument::Positional(expr(NatLiteral(3))) - ] - }))) - ] - }; -} - -#[test] -fn reserved_words() { - assert_fail!("module::item::call()", "Expected an identifier, got Colon"); -} - -#[test] -fn type_annotations() { - use ExpressionKind::*; - use TypeIdentifier::*; - - assert_ast!( - "let a = b : Int", - vec![decl(Declaration::Binding { - name: rc("a"), - constant: true, - type_anno: None, - expr: expr_anno(Value(qn!(b)), Singleton(TypeSingletonName { name: rc("Int"), params: vec![] })), - })] - ); - - assert_expr!( - "a: Int", - expr_anno(Value(qn!(a)), Singleton(TypeSingletonName { name: rc("Int"), params: vec![] })) - ); - assert_expr!( - "a: Option", - expr_anno( - Value(qn!(a)), - Singleton(TypeSingletonName { - name: rc("Option"), - params: vec![Singleton(TypeSingletonName { name: rc("Int"), params: vec![] })] - }) - ) - ); - assert_expr!( - "a: KoreanBBQSpecifier >", - expr_anno( - Value(qn!(a)), - Singleton(TypeSingletonName { - name: rc("KoreanBBQSpecifier"), - params: vec![ - Singleton(TypeSingletonName { name: rc("Kimchi"), params: vec![] }), - Singleton(TypeSingletonName { - name: rc("Option"), - params: vec![Singleton(TypeSingletonName { name: rc("Bulgogi"), params: vec![] })] - }) - ] - }) - ) - ); - assert_expr!( - "a: (Int, Yolo)", - expr_anno( - Value(qn!(a)), - Tuple(vec![ - Singleton(TypeSingletonName { name: rc("Int"), params: vec![] }), - Singleton(TypeSingletonName { - name: rc("Yolo"), - params: vec![Singleton(TypeSingletonName { name: rc("a"), params: vec![] })] - }), - ]), - ) - ); -} - -#[test] -fn type_declarations() { - use Declaration::TypeDecl; - assert_ast! { - "type Alpha = Alpha", vec![ - decl(TypeDecl { - name: TypeSingletonName { name: rc("Alpha"), params: vec![] }, - mutable: false, - body: TypeBody(vec![ - Variant { - id: Default::default(), - name: rc("Alpha"), - kind: VariantKind::UnitStruct - } - ]) - }) - ] - }; - - assert_ast!( - "type mut Kuah = Kuah", - decl(TypeDecl { - name: TypeSingletonName { name: rc("Kuah"), params: vec![] }, - mutable: true, - body: TypeBody(vec![Variant { - id: Default::default(), - name: rc("Kuah"), - kind: VariantKind::UnitStruct - }]) - }) - ); - - //TODO support this - let specs = ["type Alpha = Alpha { a: Int, b: Int }" /* "type Alpha = { a: Int, b: Int }"*/]; - for spec in specs { - assert_ast! { - spec, vec![decl(TypeDecl { - name: TypeSingletonName { name: rc("Alpha"), params: vec![] }, - mutable: false, - body: TypeBody(vec![ - Variant { - id: Default::default(), - name: rc("Alpha"), - kind: VariantKind::Record(vec![ - (rc("a"), ty_simple("Int")), - (rc("b"), ty_simple("Int")) - ]) - } - ]) - })] - }; - } - - assert_ast!( - "type Option = None | Some(T)", - vec![decl(TypeDecl { - name: TypeSingletonName { - name: rc("Option"), - params: vec![TypeIdentifier::Singleton(TypeSingletonName { name: rc("T"), params: vec![] })] - }, - mutable: false, - body: TypeBody(vec![ - Variant { id: Default::default(), name: rc("None"), kind: VariantKind::UnitStruct }, - Variant { - id: Default::default(), - name: rc("Some"), - kind: VariantKind::TupleStruct(vec![TypeIdentifier::Singleton(TypeSingletonName { - name: rc("T"), - params: vec![] - })]) - }, - ]) - })] - ); - - assert_ast!( - "type alias Alpha = Beta", - decl(Declaration::TypeAlias { alias: rc("Alpha"), original: rc("Beta") }) - ); - - assert_ast!("type Complex = Unit | Record { field: AnotherType, field2: (Nat, Int), field3: T } | Tuple(Int, (String, T))", - decl(TypeDecl { - name: TypeSingletonName { name: rc("Complex"), params: vec![ - TypeIdentifier::Singleton(TypeSingletonName { name: rc("T"), params: vec![] }), - TypeIdentifier::Singleton(TypeSingletonName { name: rc("U"), params: vec![] }), - ] }, - mutable: false, - body: TypeBody(vec![ - Variant { id: Default::default(), name: rc("Unit"), kind: VariantKind::UnitStruct }, - Variant { id: Default::default(), name: rc("Record"), kind: VariantKind::Record( - vec![ - (rc("field"), TypeIdentifier::Singleton(TypeSingletonName { name: rc("AnotherType"), params: vec![TypeIdentifier::Singleton(TypeSingletonName { name: rc("Bool"), params: vec![] })] })), - (rc("field2"), TypeIdentifier::Tuple(vec![ - TypeIdentifier::Singleton(TypeSingletonName { name: rc("Nat"), params: vec![] }), - TypeIdentifier::Singleton(TypeSingletonName { name: rc("Int"), params: vec![] }), - ] - )), - (rc("field3"), TypeIdentifier::Singleton(TypeSingletonName { name: rc("T"), params: vec![] })), - ] - )}, - Variant { id: Default::default(), name: rc("Tuple"), kind: VariantKind::TupleStruct( - vec![ - TypeIdentifier::Singleton(TypeSingletonName { name: rc("Int"), params: vec![] }), - TypeIdentifier::Tuple(vec![ - TypeIdentifier::Singleton(TypeSingletonName { name: rc("String"), params: vec![] }), - TypeIdentifier::Singleton(TypeSingletonName { name: rc("T"), params: vec![] }), - ]) - ] - )}, - ]), - })); -} - -#[test] -fn declarations() { - use ExpressionKind::*; - - assert_ast!( - "let q_q = Yolo::Swaggins", - vec![decl(Declaration::Binding { - name: rc("q_q"), - constant: true, - type_anno: None, - expr: expr(Value(qn!(Yolo, Swaggins))) - })] - ); -} - -#[test] -fn bindings() { - use ExpressionKind::*; - - assert_ast!( - "let mut a = 10", - vec![decl(Declaration::Binding { - name: rc("a"), - constant: false, - type_anno: None, - expr: expr(NatLiteral(10)), - })] - ); - - assert_ast!( - "let a = 2 + a", - vec![stmt(StatementKind::Declaration(Declaration::Binding { - name: rc("a"), - constant: true, - type_anno: None, - expr: binop("+", expr(NatLiteral(2)), expr(Value(qn!(a)))), - }))] - ); - - assert_ast!( - "let a: Nat = 2", - vec![stmt(StatementKind::Declaration(Declaration::Binding { - name: rc("a"), - constant: true, - type_anno: Some(TypeIdentifier::Singleton(TypeSingletonName { name: rc("Nat"), params: vec![] })), - expr: expr(NatLiteral(2)), - }))] - ); -} - -#[test] -fn functions() { - use ExpressionKind::*; - assert_ast!( - "fn oi()", - vec![stmt(StatementKind::Declaration(Declaration::FuncSig(Signature { - name: rc("oi"), - operator: false, - params: vec![], - type_anno: None - })))] - ); - - assert_ast!( - "oi()", - vec![stmt(StatementKind::Expression(expr(Call { f: bx(expr(Value(qn!(oi)))), arguments: vec![] })))] - ); - - assert_expr!( - "oi(a, 2+2)", - expr(Call { - f: bx(expr(Value(qn!(oi)))), - arguments: vec![ - InvocationArgument::Positional(expr(Value(qn!(a)))), - InvocationArgument::Positional(binop("+", expr(NatLiteral(2)), expr(NatLiteral(2)))), - ] - }) - ); - assert_fail!("a(b,,c)", "Expected a literal expression, got Comma"); - - assert_ast!( - "fn a(b, c: Int): Int", - vec![stmt(StatementKind::Declaration(Declaration::FuncSig(Signature { - name: rc("a"), - operator: false, - params: vec![ - FormalParam { name: rc("b"), default: None, anno: None }, - FormalParam { - name: rc("c"), - default: None, - anno: Some(TypeIdentifier::Singleton(TypeSingletonName { - name: rc("Int"), - params: vec![] - })), - }, - ], - type_anno: Some(TypeIdentifier::Singleton(TypeSingletonName { name: rc("Int"), params: vec![] })), - })))] - ); -} - -#[test] -fn functions_with_different_whitespace() { - use ExpressionKind::*; - - let a = "fn a(x) { x() }"; - let b = "fn a(x) {\n x() }"; - let c = r#" - - fn a(x) { - - - x() - - - } - - "#; - - for item in [a, b, c].iter() { - assert_ast!( - item, - vec![fn_decl( - Signature { - name: rc("a"), - operator: false, - type_anno: None, - params: vec![FormalParam { name: rc("x"), default: None, anno: None }] - }, - vec![stmt(StatementKind::Expression(expr(Call { - f: bx(expr(Value(qn!(x)))), - arguments: vec![], - })))] - .into() - )] - ); - } -} - -#[test] -fn functions_with_default_args() { - use ExpressionKind::*; - - assert_ast!( - "fn func(x: Int, y: Int = 4) { }", - vec![fn_decl( - Signature { - name: rc("func"), - operator: false, - type_anno: None, - params: vec![ - FormalParam { name: rc("x"), anno: Some(ty_simple("Int")), default: None }, - FormalParam { - name: rc("y"), - anno: Some(ty_simple("Int")), - default: Some(expr(NatLiteral(4))) - }, - ], - }, - vec![].into() - )] - ); -} - -#[test] -fn interface() { - let glue = TypeIdentifier::Singleton(TypeSingletonName { name: rc("Glue"), params: vec![] }); - assert_ast!( - "interface Unglueable { fn unglue(a: Glue); fn mar(): Glue }", - vec![decl(Declaration::Interface { - name: rc("Unglueable"), - signatures: vec![ - Signature { - name: rc("unglue"), - operator: false, - params: vec![FormalParam { name: rc("a"), default: None, anno: Some(glue.clone()) },], - type_anno: None, - }, - Signature { name: rc("mar"), operator: false, params: vec![], type_anno: Some(glue) }, - ], - })] - ); -} - -#[test] -fn impls() { - use Declaration::{FuncSig, Impl}; - - assert_ast!( - "impl Heh { fn yolo(); fn swagg(); }", - vec![decl(Impl { - type_name: ty_simple("Heh"), - interface_name: None, - block: vec![ - FuncSig(Signature { name: rc("yolo"), operator: false, params: vec![], type_anno: None }), - FuncSig(Signature { name: rc("swagg"), operator: false, params: vec![], type_anno: None }) - ] - })] - ); - - assert_ast!( - "impl Heh { fn yolo(); fn swagg(); }", - vec![decl(Impl { - type_name: TypeIdentifier::Singleton(TypeSingletonName { - name: rc("Heh"), - params: vec![ty_simple("X")] - }), - interface_name: None, - block: vec![ - FuncSig(Signature { name: rc("yolo"), operator: false, params: vec![], type_anno: None }), - FuncSig(Signature { name: rc("swagg"), operator: false, params: vec![], type_anno: None }) - ] - })] - ); - - assert_ast!( - "impl Heh for Saraz { fn yolo(); fn swagg(); }", - vec![decl(Impl { - type_name: ty_simple("Saraz"), - interface_name: Some(TypeSingletonName { name: rc("Heh"), params: vec![] }), - block: vec![ - FuncSig(Signature { name: rc("yolo"), operator: false, params: vec![], type_anno: None }), - FuncSig(Signature { name: rc("swagg"), operator: false, params: vec![], type_anno: None }) - ] - })] - ); - - assert_ast!( - "impl Heh for (Int, Codepoint) {}", - vec![decl(Impl { - type_name: TypeIdentifier::Tuple(vec![ty_simple("Int"), ty_simple("Codepoint")]), - interface_name: Some(TypeSingletonName { name: rc("Heh"), params: vec![ty_simple("T")] }), - block: vec![] - })] - ); -} - -#[test] -fn annotations() { - use ExpressionKind::*; - - assert_ast! { - r#" - @test_annotation - fn some_function() { - - }"#, - vec![decl(Declaration::Annotation { - name: rc("test_annotation"), - arguments: vec![] - }), - fn_decl(Signature { name: rc("some_function"), operator: false, params: vec![], type_anno: None }, - vec![].into()) - ] - }; - - assert_ast! { - r#" - @test_annotation(some,value) - fn some_function() { - - }"#, - vec![decl(Declaration::Annotation { - name: rc("test_annotation"), - arguments: vec![expr(Value(qn!(some))), expr(Value(qn!(value)))] - }), - fn_decl(Signature { name: rc("some_function"), operator: false, params: vec![], type_anno: None }, - vec![].into()) - ] - }; -} - -#[test] -fn modules() { - assert_ast! { - r#" - module ephraim { - let mut a = 10 - fn nah() { 33 } - } - "#, - vec![stmt(StatementKind::Module(ModuleSpecifier { - name: rc("ephraim"), - contents: vec![ - decl(Declaration::Binding { - name: rc("a"), constant: false, type_anno: None, - expr: expr(ExpressionKind::NatLiteral(10)) - }), - fn_decl(Signature { name: rc("nah"), operator: false, params: vec![], type_anno: None }, - vec![stmt(StatementKind::Expression(expr(ExpressionKind::NatLiteral(33))))].into()), - ].into() - }))] - }; -} - -#[test] -fn imports() { - assert_ast! { - "import harbinger::draughts::Norgleheim", - vec![stmt(StatementKind::Import(ImportSpecifier { - id: ItemId::default(), - path_components: vec![rc("harbinger"), rc("draughts"), rc("Norgleheim")], - imported_names: ImportedNames::LastOfPath - }))] - }; - - assert_ast! { - "import harbinger::draughts::{Norgleheim, Xraksenlaigar}", - vec![stmt(StatementKind::Import(ImportSpecifier { - id: ItemId::default(), - path_components: vec![rc("harbinger"), rc("draughts")], - imported_names: ImportedNames::List(vec![ - rc("Norgleheim"), rc("Xraksenlaigar")]) - }))] - }; - - assert_ast! { - "import bespouri::{}", - vec![stmt(StatementKind::Import(ImportSpecifier { - id: Default::default(), - path_components: vec![rc("bespouri")], - imported_names: ImportedNames::List(vec![]), - }))] - }; - - assert_ast! { - "import bespouri::*", - vec![stmt(StatementKind::Import(ImportSpecifier { - id: Default::default(), - path_components: vec![rc("bespouri")], - imported_names: ImportedNames::All, - }))] - }; -} - -#[test] -fn if_exprs() { - use ExpressionKind::*; - assert_expr!( - "if a() then { tuah(); }", - expr(IfExpression { - discriminator: Some(bx(expr(Call { f: bx(expr(Value(qn!(a)))), arguments: vec![] }))), - body: bx(IfExpressionBody::SimpleConditional { - then_case: vec![exst(Call { f: bx(expr(Value(qn!(tuah)))), arguments: vec![] })].into(), - else_case: None, - }) - }) - ); - - assert_expr!( - "if a then b else c", - expr(IfExpression { - discriminator: Some(bx(expr(Value(qn!(a))))), - body: bx(IfExpressionBody::SimpleConditional { - then_case: vec![exst(Value(qn!(b)))].into(), - else_case: Some(vec![exst(Value(qn!(c)))].into()), - }) - }) - ); - - assert_expr!( - r#" - if true then { - let a = 10 - b - } else { - c - }"#, - expr(IfExpression { - discriminator: Some(bx(expr(BoolLiteral(true)))), - body: bx(IfExpressionBody::SimpleConditional { - then_case: vec![ - decl(Declaration::Binding { - name: rc("a"), - constant: true, - type_anno: None, - expr: expr(NatLiteral(10)) - }), - exst(Value(qn!(b))), - ] - .into(), - else_case: Some(vec![exst(Value(qn!(c))),].into()) - }) - }) - ); -} - -#[test] -fn pattern_matching() { - use ExpressionKind::*; - - for item in ["if x is Some(a) then { 4 } else { 9 }", "if x is Some(a) then 4 else 9"] { - assert_expr!( - item, - expr(IfExpression { - discriminator: Some(bx(expr(Value(qn!(x))))), - body: bx(IfExpressionBody::SimplePatternMatch { - pattern: Pattern::TupleStruct(qn!(Some), vec![Pattern::VarOrName(qn!(a))]), - then_case: vec![exst(NatLiteral(4))].into(), - else_case: Some(vec![exst(NatLiteral(9))].into()), - }) - }) - ); - } - - assert_expr!( - "if x is Something { a, b: x } then { 4 } else { 9 }", - expr(IfExpression { - discriminator: Some(bx(expr(Value(qn!(x))))), - body: bx(IfExpressionBody::SimplePatternMatch { - pattern: Pattern::Record( - qn!(Something), - vec![ - (rc("a"), Pattern::Literal(PatternLiteral::StringPattern(rc("a")))), - (rc("b"), Pattern::VarOrName(qn!(x))) - ] - ), - then_case: vec![exst(NatLiteral(4))].into(), - else_case: Some(vec![exst(NatLiteral(9))].into()), - }) - }) - ); - - assert_expr!( - "if x is -1 then 1 else 2", - expr(IfExpression { - discriminator: Some(bx(expr(Value(qn!(x))))), - body: bx(IfExpressionBody::SimplePatternMatch { - pattern: Pattern::Literal(PatternLiteral::NumPattern { neg: true, num: NatLiteral(1) }), - then_case: vec![exst(NatLiteral(1))].into(), - else_case: Some(vec![exst(NatLiteral(2))].into()), - }) - }) - ); - - assert_expr!( - "if x is true then 1 else 2", - expr(IfExpression { - discriminator: Some(bx(expr(Value(qn!(x))))), - body: bx(IfExpressionBody::SimplePatternMatch { - pattern: Pattern::Literal(PatternLiteral::BoolPattern(true)), - then_case: vec![exst(NatLiteral(1))].into(), - else_case: Some(vec![exst(NatLiteral(2))].into()), - }) - }) - ); - - assert_expr!( - "if x { is 1 then 5, else 20 }", - expr(IfExpression { - discriminator: Some(bx(expr(Value(qn!(x))))), - body: bx(IfExpressionBody::CondList(vec![ - ConditionArm { - condition: Condition::Pattern(Pattern::Literal(PatternLiteral::NumPattern { - neg: false, - num: NatLiteral(1) - })), - guard: None, - body: vec![exst(NatLiteral(5))].into(), - }, - ConditionArm { - condition: Condition::Else, - guard: None, - body: vec![exst(NatLiteral(20))].into(), - }, - ])) - }) - ); - - assert_expr!( - r#"if x is "gnosticism" then 1 else 2"#, - expr(IfExpression { - discriminator: Some(bx(expr(Value(qn!(x))))), - body: bx(IfExpressionBody::SimplePatternMatch { - pattern: Pattern::Literal(PatternLiteral::StringPattern(rc("gnosticism"))), - then_case: vec![exst(NatLiteral(1))].into(), - else_case: Some(vec![exst(NatLiteral(2))].into()), - }) - }) - ); - - assert_expr! { - r#" -if (45, "panda", false, 2.2) { - is (49, "pablo", _, 28.4) then "no" - is (_, "panda", _, -2.2) then "yes" - is _ then "maybe" -}"#, - expr( - IfExpression { - discriminator: Some(bx(expr(TupleLiteral(vec![ - expr(NatLiteral(45)), expr(StringLiteral(rc("panda"))), expr(BoolLiteral(false)), expr(FloatLiteral(2.2)) - ])))), - body: bx(IfExpressionBody::CondList(vec![ - ConditionArm { - condition: Condition::Pattern(Pattern::TuplePattern( - vec![ - Pattern::Literal(PatternLiteral::NumPattern { neg: false, num: NatLiteral(49) }), - Pattern::Literal(PatternLiteral::StringPattern(rc("pablo"))), - Pattern::Ignored, - Pattern::Literal(PatternLiteral::NumPattern { neg: false, num: FloatLiteral(28.4) }), - ] - )), - guard: None, - body: vec![stmt(StatementKind::Expression(expr(StringLiteral(rc("no")))))].into(), - }, - ConditionArm { - condition: Condition::Pattern(Pattern::TuplePattern( - vec![ - Pattern::Ignored, - Pattern::Literal(PatternLiteral::StringPattern(rc!(panda))), - Pattern::Ignored, - Pattern::Literal(PatternLiteral::NumPattern { neg: true, num: FloatLiteral(2.2) }), - ] - )), - guard: None, - body: vec![stmt(StatementKind::Expression(expr(StringLiteral(rc("yes")))))].into(), - }, - ConditionArm { - condition: Condition::Pattern(Pattern::Ignored), - guard: None, - body: vec![exst(StringLiteral(rc("maybe")))].into(), - }, - ])) - } - ) - }; -} diff --git a/schala-lang/language/src/parsing/test.rs b/schala-lang/language/src/parsing/test.rs index 9df787e..1ea2899 100644 --- a/schala-lang/language/src/parsing/test.rs +++ b/schala-lang/language/src/parsing/test.rs @@ -1,173 +1,1207 @@ #![cfg(test)] #![allow(clippy::upper_case_acronyms)] #![allow(clippy::vec_init_then_push)] - -use pretty_assertions::assert_eq; +//use test_case::test_case; use std::rc::Rc; -use crate::tokenizing::Location; -use super::{Parser, ParseResult, tokenize}; -use crate::ast::*; -use super::Declaration::*; -use super::Signature; -use super::TypeIdentifier::*; -use super::TypeSingletonName; -use super::ExpressionKind::*; -use super::VariantKind::*; +use pretty_assertions::assert_eq; + +use super::{tokenize, ParseResult, Parser}; +use crate::{ast::*, tokenizing::Location}; + +fn rc(s: &str) -> Rc { + Rc::new(s.to_owned()) +} + +fn bx(item: T) -> Box { + Box::new(item) +} fn make_parser(input: &str) -> Parser { - let tokens: Vec = tokenize(input); - let mut parser = super::Parser::new(); - parser.add_new_tokens(tokens); - parser + let tokens: Vec = tokenize(input); + let mut parser = super::Parser::new(); + parser.add_new_tokens(tokens); + parser } fn parse(input: &str) -> ParseResult { - let mut parser = make_parser(input); - parser.parse() + let mut parser = make_parser(input); + parser.parse() } -//TODO maybe can be const? -fn make_statement(kind: StatementKind) -> Statement { - Statement { - location: Location::default(), - id: ItemId::default(), - kind, - } +fn stmt(kind: StatementKind) -> Statement { + Statement { location: Location::default(), id: ItemId::default(), kind } } -macro_rules! bx { - ($e:expr) => { - Box::new($e) +fn exst(kind: ExpressionKind) -> Statement { + Statement { + location: Location::default(), + id: ItemId::default(), + kind: StatementKind::Expression(expr(kind)), + } +} + +fn decl(declaration: Declaration) -> Statement { + Statement { + location: Location::default(), + id: ItemId::default(), + kind: StatementKind::Declaration(declaration), + } +} + +fn fn_decl(sig: Signature, stmts: Block) -> Statement { + Statement { + kind: StatementKind::Declaration(Declaration::FuncDecl(sig, stmts)), + location: Default::default(), + id: Default::default(), + } +} + +fn expr_anno(kind: ExpressionKind, anno: TypeIdentifier) -> Expression { + Expression { id: ItemId::default(), kind, type_anno: Some(anno) } +} + +fn expr(kind: ExpressionKind) -> Expression { + Expression { id: ItemId::default(), kind, type_anno: None } +} + +fn binop(sigil: &str, lhs: Expression, rhs: Expression) -> Expression { + Expression { + id: Default::default(), + type_anno: None, + kind: ExpressionKind::BinExp(BinOp::from_sigil(sigil), Box::new(lhs), Box::new(rhs)), + } +} + +fn prefixop(sigil: &str, exp: Expression) -> Expression { + Expression { + id: Default::default(), + type_anno: None, + kind: ExpressionKind::PrefixExp(PrefixOp::from_sigil(sigil), Box::new(exp)), + } +} + +macro_rules! qn { + ( $( $component:ident),* ) => { + { + let mut components = vec![]; + $( + components.push(rc(stringify!($component))); + )* + QualifiedName { components, id: Default::default() } + } + }; +} + +fn ty_simple(name: &str) -> TypeIdentifier { + TypeIdentifier::Singleton(TypeSingletonName { name: rc(name), params: vec![] }) +} + +macro_rules! assert_ast { + ($input:expr, $statements:expr) => { + let ast = parse($input).unwrap(); + let expected = AST { id: Default::default(), statements: $statements.into() }; + println!("Expected: {}", expected); + println!("Actual: {}", ast); + assert_eq!(ast, expected); }; } -macro_rules! parse_test { - ($string:expr, $correct:expr) => { - assert_eq!(parse($string).unwrap(), $correct) - }; -} -macro_rules! parse_test_wrap_ast { - ($string:expr, $correct:expr) => { parse_test!($string, AST { id: Default::default(), statements: vec![$correct].into() }) } -} -macro_rules! qname { - ( $( $component:expr),* ) => { - { - let mut components = vec![]; - $( - components.push(rc!($component)); - )* - QualifiedName { components, id: Default::default() } - } - }; -} -macro_rules! val { - ($var:expr) => { Value(QualifiedName { components: vec![Rc::new($var.to_string())], id: Default::default() }) }; -} -macro_rules! ty { - ($name:expr) => { Singleton(tys!($name)) } -} -macro_rules! tys { - ($name:expr) => { TypeSingletonName { name: Rc::new($name.to_string()), params: vec![] } }; +macro_rules! assert_fail { + ($input:expr, $failure:expr) => { + let err = parse($input).unwrap_err(); + assert_eq!(err.msg, $failure); + }; } -macro_rules! decl { - ($expr_type:expr) => { - make_statement(StatementKind::Declaration($expr_type)) - }; +macro_rules! assert_expr { + ($input:expr, $correct:expr) => { + let mut parser = make_parser($input); + assert_eq!(parser.expression().unwrap(), $correct); + }; } -macro_rules! ex { - ($expr_type:expr) => { Expression::new(Default::default(), $expr_type) }; - ($expr_type:expr, $type_anno:expr) => { Expression::with_anno(Default::default(), $expr_type, $type_anno) }; - (s $expr_text:expr) => { - { - let mut parser = make_parser($expr_text); - parser.expression().unwrap() - } - }; +macro_rules! assert_fail_expr { + ($input:expr, $failure:expr) => { + let mut parser = make_parser($input); + let err = parser.expression().unwrap_err(); + assert_eq!(err.msg, $failure); + }; } +#[test] +fn basic_literals() { + use ExpressionKind::*; -macro_rules! exst { - ($expr_type:expr) => { make_statement(StatementKind::Expression(Expression::new(Default::default(), $expr_type).into())) }; - ($expr_type:expr, $type_anno:expr) => { make_statement(StatementKind::Expression(Expression::with_anno(Default::default(), $expr_type, $type_anno).into())) }; - ($op:expr, $lhs:expr, $rhs:expr) => { make_statement(StatementKind::Expression(ex!(binexp!($op, $lhs, $rhs)))) }; - (s $statement_text:expr) => { - { - let mut parser = make_parser($statement_text); - parser.statement().unwrap() - } - } + assert_expr!(".2", expr(FloatLiteral(0.2))); + assert_expr!("8.1", expr(FloatLiteral(8.1))); + assert_expr!("0b010", expr(NatLiteral(2))); + assert_expr!("0b0_1_0", expr(NatLiteral(2))); + assert_expr!("0xff", expr(NatLiteral(255))); + assert_expr!("0x032f", expr(NatLiteral(815))); + assert_expr!("0xf_f_", expr(NatLiteral(255))); + assert_expr!("false", expr(BoolLiteral(false))); + assert_expr!("true", expr(BoolLiteral(true))); + assert_expr!(r#""hello""#, expr(StringLiteral(rc("hello")))); } - #[test] -fn parsing_block_expressions() { - parse_test_wrap_ast! { - "if a() then { b(); c() }", exst!( - IfExpression { - discriminator: Some(bx! { - ex!(Call { f: bx!(ex!(val!("a"))), arguments: vec![]}) - }), - body: bx! { - IfExpressionBody::SimpleConditional { - then_case: vec![exst!(Call { f: bx!(ex!(val!("b"))), arguments: vec![]}), exst!(Call { f: bx!(ex!(val!("c"))), arguments: vec![] })].into(), - else_case: None, - } - } - } - ) - }; +fn list_literals() { + use ExpressionKind::*; - parse_test_wrap_ast! { - "if a() then { b(); c() } else { q }", exst!( - IfExpression { - discriminator: Some(bx! { - ex!(Call { f: bx!(ex!(val!("a"))), arguments: vec![]}) - }), - body: bx! { - IfExpressionBody::SimpleConditional { - then_case: vec![exst!(Call { f: bx!(ex!(val!("b"))), arguments: vec![]}), exst!(Call { f: bx!(ex!(val!("c"))), arguments: vec![] })].into(), - else_case: Some(vec![exst!(val!("q"))].into()), - } - } - } - ) - }; + assert_expr!("[]", expr(ListLiteral(vec![]))); + assert_expr!("[1,2]", expr(ListLiteral(vec![expr(NatLiteral(1)), expr(NatLiteral(2)),]))); +} - /* - parse_test!("if a() then { b(); c() }", AST(vec![exst!( - IfExpression(bx!(ex!(Call { f: bx!(ex!(val!("a"))), arguments: vec![]})), - vec![exst!(Call { f: bx!(ex!(val!("b"))), arguments: vec![]}), exst!(Call { f: bx!(ex!(val!("c"))), arguments: vec![] })], - None) - )])); - parse_test!(r#" +#[test] +fn binexps() { + use ExpressionKind::*; + use StatementKind::Expression; + + assert_expr!("0xf_f_+1", binop("+", expr(NatLiteral(255)), expr(NatLiteral(1)))); + assert_eq!( + parse("3; 4; 4.3").unwrap(), + AST { + id: Default::default(), + statements: vec![ + stmt(Expression(expr(NatLiteral(3)))), + stmt(Expression(expr(NatLiteral(4)))), + stmt(Expression(expr(FloatLiteral(4.3)))), + ] + .into() + } + ); + + assert_expr!( + "1 + 2 * 3", + binop("+", expr(NatLiteral(1)), binop("*", expr(NatLiteral(2)), expr(NatLiteral(3)))) + ); + assert_expr!( + "1 * 2 + 3", + binop("+", binop("*", expr(NatLiteral(1)), expr(NatLiteral(2))), expr(NatLiteral(3))) + ); + assert_expr!("1 && 2", binop("&&", expr(NatLiteral(1)), expr(NatLiteral(2)))); + assert_expr!( + "1 + 2 * 3 + 4", + binop( + "+", + binop("+", expr(NatLiteral(1)), binop("*", expr(NatLiteral(2)), expr(NatLiteral(3)))), + expr(NatLiteral(4)) + ) + ); + assert_expr!( + "(1 + 2) * 3", + binop("*", binop("+", expr(NatLiteral(1)), expr(NatLiteral(2))), expr(NatLiteral(3))) + ); + assert_expr!(".1 + .2", binop("+", expr(FloatLiteral(0.1)), expr(FloatLiteral(0.2)))); + assert_expr!("1 / 2.", binop("/", expr(NatLiteral(1)), expr(FloatLiteral(2.)))); +} + +#[test] +fn prefix_exps() { + use ExpressionKind::*; + + assert_expr!("-3", prefixop("-", expr(NatLiteral(3)))); + assert_expr!("-0.2", prefixop("-", expr(FloatLiteral(0.2)))); + assert_expr!("!3", prefixop("!", expr(NatLiteral(3)))); + assert_expr!("!t", prefixop("!", expr(Value(qn!(t))))); + assert_expr!("a <- -b", binop("<-", expr(Value(qn!(a))), prefixop("-", expr(Value(qn!(b)))))); + assert_expr!("a <--b", binop("<--", expr(Value(qn!(a))), expr(Value(qn!(b))))); +} + +#[test] +fn operators() { + use ExpressionKind::*; + + assert_expr!("a <- 1", binop("<-", expr(Value(qn!(a))), expr(NatLiteral(1)))); + assert_expr!("a || 1", binop("||", expr(Value(qn!(a))), expr(NatLiteral(1)))); + assert_expr!("a <> 1", binop("<>", expr(Value(qn!(a))), expr(NatLiteral(1)))); +} + +#[test] +fn accessors() { + use ExpressionKind::*; + + assert_expr!("a.b", expr(Access { name: rc("b"), expr: bx(expr(Value(qn!(a)))) })); + assert_expr!( + "a.b.c", + expr(Access { + name: rc("c"), + expr: bx(expr(Access { name: rc("b"), expr: bx(expr(Value(qn!(a)))) })) + }) + ); + assert_expr!( + "a.b.c(3)", + expr(Call { + f: bx(expr(Access { + name: rc("c"), + expr: bx(expr(Access { name: rc("b"), expr: bx(expr(Value(qn!(a)))) })) + })), + arguments: vec![InvocationArgument::Positional(expr(NatLiteral(3)))], + }) + ); + assert_expr!( + "a.b().c", + expr(Access { + name: rc("c"), + expr: bx(expr(Call { + f: bx(expr(Access { name: rc("b"), expr: bx(expr(Value(qn!(a)))) })), + arguments: vec![] + })) + }) + ); +} + +#[test] +fn tuples() { + use ExpressionKind::*; + + assert_expr!("()", expr(TupleLiteral(vec![]))); + assert_expr!( + r#"("hella", 34)"#, + expr(TupleLiteral(vec![expr(StringLiteral(rc("hella"))), expr(NatLiteral(34))])) + ); + assert_expr!( + r#"(1+2, "slough")"#, + expr(TupleLiteral(vec![ + binop("+", expr(NatLiteral(1)), expr(NatLiteral(2))), + expr(StringLiteral(rc("slough"))), + ])) + ); +} + +#[test] +fn identifiers() { + use ExpressionKind::*; + + assert_expr!("a", expr(Value(qn!(a)))); + assert_expr!("some_value", expr(Value(qn!(some_value)))); + assert_expr!("alpha::beta::gamma", expr(Value(qn!(alpha, beta, gamma)))); + assert_expr!("a + b", binop("+", expr(Value(qn!(a))), expr(Value(qn!(b))))); + assert_expr!("None", expr(Value(qn!(None)))); + assert_expr!( + "thing::item::call()", + expr(Call { f: bx(expr(Value(qn!(thing, item, call)))), arguments: vec![] }) + ); +} + +#[test] +fn named_struct() { + use ExpressionKind::*; + assert_expr!( + "Pandas { a: x + y }", + expr(NamedStruct { + name: qn!(Pandas), + fields: vec![(rc("a"), binop("+", expr(Value(qn!(x))), expr(Value(qn!(y)))))] + }) + ); + assert_expr!( + "Trousers { a:1, b:800 }", + expr(NamedStruct { + name: qn!(Trousers), + fields: vec![(rc("a"), expr(NatLiteral(1))), (rc("b"), expr(NatLiteral(800)))] + }) + ); +} + +#[test] +fn index() { + use ExpressionKind::*; + assert_expr!( + "a[b,c]", + expr(Index { + indexee: bx(expr(Value(qn!(a)))), + indexers: vec![expr(Value(qn!(b))), expr(Value(qn!(c)))] + }) + ); + assert_expr!( + "perspicacity()[a]", + expr(Index { + indexee: bx(expr(Call { f: bx(expr(Value(qn!(perspicacity)))), arguments: vec![] })), + indexers: vec![expr(Value(qn!(a)))] + }) + ); + + let a = expr(Call { f: bx(expr(Value(qn!(a)))), arguments: vec![] }); + let b = expr(Index { indexee: bx(a), indexers: vec![expr(Value(qn!(b)))] }); + let c = expr(Call { f: bx(b), arguments: vec![] }); + let d = expr(Index { indexee: bx(c), indexers: vec![expr(Value(qn!(d)))] }); + assert_expr!("a()[b]()[d]", d); + + assert_fail_expr!("a[]", "Empty index expressions are not allowed"); +} + +#[test] +fn while_expression() { + use ExpressionKind::*; + + assert_expr!("while { }", expr(WhileExpression { condition: None, body: Block::default() })); + assert_expr!( + "while a == b { }", + expr(WhileExpression { + condition: Some(bx(binop("==", expr(Value(qn!(a))), expr(Value(qn!(b)))))), + body: Block::default() + }) + ); +} + +#[test] +fn for_expression() { + use ExpressionKind::*; + + assert_expr!( + "for { a <- garodzny::maybeValue } return 1", + expr(ForExpression { + enumerators: vec![Enumerator { id: rc("a"), generator: expr(Value(qn!(garodzny, maybeValue))) }], + body: bx(ForBody::MonadicReturn(expr(NatLiteral(1)))) + }) + ); + + assert_expr!( + "for n <- someRange { f(n); }", + expr(ForExpression { + enumerators: vec![Enumerator { id: rc("n"), generator: expr(Value(qn!(someRange))) }], + body: bx(ForBody::StatementBlock( + vec![stmt(StatementKind::Expression(expr(Call { + f: bx(expr(Value(qn!(f)))), + arguments: vec![InvocationArgument::Positional(expr(Value(qn!(n))))], + }))),] + .into() + )), + }) + ); +} + +#[test] +fn lambda_expressions() { + use ExpressionKind::*; + + assert_expr!( + r#"\(x) { x + 1}"#, + expr(Lambda { + params: vec![FormalParam { name: rc!(x), anno: None, default: None }], + type_anno: None, + body: + vec![stmt(StatementKind::Expression(binop("+", expr(Value(qn!(x))), expr(NatLiteral(1))))),] + .into() + }) + ); + + assert_expr!( + r#"\ (x: Int, y) { a;b;c;}"#, + expr(Lambda { + params: vec![ + FormalParam { name: rc!(x), anno: Some(ty_simple("Int")), default: None }, + FormalParam { name: rc!(y), anno: None, default: None }, + ], + type_anno: None, + body: vec![ + stmt(StatementKind::Expression(expr(Value(qn!(a))))), + stmt(StatementKind::Expression(expr(Value(qn!(b))))), + stmt(StatementKind::Expression(expr(Value(qn!(c))))), + ] + .into() + }) + ); + + assert_expr!( + r#"\(x){y}(1)"#, + expr(Call { + f: bx(expr(Lambda { + params: vec![FormalParam { name: rc!(x), anno: None, default: None },], + type_anno: None, + body: vec![stmt(StatementKind::Expression(expr(Value(qn!(y))))),].into() + })), + arguments: vec![InvocationArgument::Positional(expr(NatLiteral(1)))], + }) + ); + + assert_expr!( + r#"\(x: Int): String { "q" }"#, + expr(Lambda { + params: vec![FormalParam { name: rc!(x), anno: Some(ty_simple("Int")), default: None },], + type_anno: Some(TypeIdentifier::Singleton(TypeSingletonName { + name: rc("String"), + params: vec![] + })), + body: vec![stmt(StatementKind::Expression(expr(StringLiteral(rc("q"))))),].into() + }) + ); +} + +#[test] +fn single_param_lambda() { + use ExpressionKind::*; + + assert_expr!( + r#"\x { x + 10 }"#, + expr(Lambda { + params: vec![FormalParam { name: rc!(x), anno: None, default: None },], + type_anno: None, + body: vec![stmt(StatementKind::Expression(binop( + "+", + expr(Value(qn!(x))), + expr(NatLiteral(10)) + )))] + .into() + }) + ); + + assert_expr!( + r#"\x: Int { x + 10 }"#, + expr(Lambda { + params: vec![FormalParam { name: rc!(x), anno: Some(ty_simple("Int")), default: None },], + type_anno: None, + body: vec![stmt(StatementKind::Expression(binop( + "+", + expr(Value(qn!(x))), + expr(NatLiteral(10)) + )))] + .into() + }) + ); +} + +#[test] +fn complex_lambdas() { + use ExpressionKind::*; + + assert_ast! { + r#"fn wahoo() { let a = 10; \(x) { x + a } }; + wahoo()(3) "#, + vec![ + fn_decl(Signature { name: rc("wahoo"), operator: false, type_anno: None, params: vec![] }, + vec![ + decl(Declaration::Binding { + name: rc("a"), + constant: true, + type_anno: None, + expr: expr(NatLiteral(10)) + }), + stmt(StatementKind::Expression(expr(Lambda { + params: vec![ + FormalParam { name: rc("x"), default: None, anno: None } + ], + type_anno: None, + body: vec![ + stmt(StatementKind::Expression(binop("+", expr(Value(qn!(x))), expr(Value(qn!(a)))))), + ].into() + }))), + ].into()), + stmt(StatementKind::Expression(expr(Call { + f: bx(expr(Call { + f: bx(expr(Value(qn!(wahoo)))), + arguments: vec![] })), + arguments: vec![ + InvocationArgument::Positional(expr(NatLiteral(3))) + ] + }))) + ] + }; +} + +#[test] +fn reserved_words() { + assert_fail!("module::item::call()", "Expected an identifier, got Colon"); +} + +#[test] +fn type_annotations() { + use ExpressionKind::*; + use TypeIdentifier::*; + + assert_ast!( + "let a = b : Int", + vec![decl(Declaration::Binding { + name: rc("a"), + constant: true, + type_anno: None, + expr: expr_anno(Value(qn!(b)), Singleton(TypeSingletonName { name: rc("Int"), params: vec![] })), + })] + ); + + assert_expr!( + "a: Int", + expr_anno(Value(qn!(a)), Singleton(TypeSingletonName { name: rc("Int"), params: vec![] })) + ); + assert_expr!( + "a: Option", + expr_anno( + Value(qn!(a)), + Singleton(TypeSingletonName { + name: rc("Option"), + params: vec![Singleton(TypeSingletonName { name: rc("Int"), params: vec![] })] + }) + ) + ); + assert_expr!( + "a: KoreanBBQSpecifier >", + expr_anno( + Value(qn!(a)), + Singleton(TypeSingletonName { + name: rc("KoreanBBQSpecifier"), + params: vec![ + Singleton(TypeSingletonName { name: rc("Kimchi"), params: vec![] }), + Singleton(TypeSingletonName { + name: rc("Option"), + params: vec![Singleton(TypeSingletonName { name: rc("Bulgogi"), params: vec![] })] + }) + ] + }) + ) + ); + assert_expr!( + "a: (Int, Yolo)", + expr_anno( + Value(qn!(a)), + Tuple(vec![ + Singleton(TypeSingletonName { name: rc("Int"), params: vec![] }), + Singleton(TypeSingletonName { + name: rc("Yolo"), + params: vec![Singleton(TypeSingletonName { name: rc("a"), params: vec![] })] + }), + ]), + ) + ); +} + +#[test] +fn type_declarations() { + use Declaration::TypeDecl; + assert_ast! { + "type Alpha = Alpha", vec![ + decl(TypeDecl { + name: TypeSingletonName { name: rc("Alpha"), params: vec![] }, + mutable: false, + body: TypeBody(vec![ + Variant { + id: Default::default(), + name: rc("Alpha"), + kind: VariantKind::UnitStruct + } + ]) + }) + ] + }; + + assert_ast!( + "type mut Kuah = Kuah", + decl(TypeDecl { + name: TypeSingletonName { name: rc("Kuah"), params: vec![] }, + mutable: true, + body: TypeBody(vec![Variant { + id: Default::default(), + name: rc("Kuah"), + kind: VariantKind::UnitStruct + }]) + }) + ); + + //TODO support this + let specs = ["type Alpha = Alpha { a: Int, b: Int }" /* "type Alpha = { a: Int, b: Int }"*/]; + for spec in specs { + assert_ast! { + spec, vec![decl(TypeDecl { + name: TypeSingletonName { name: rc("Alpha"), params: vec![] }, + mutable: false, + body: TypeBody(vec![ + Variant { + id: Default::default(), + name: rc("Alpha"), + kind: VariantKind::Record(vec![ + (rc("a"), ty_simple("Int")), + (rc("b"), ty_simple("Int")) + ]) + } + ]) + })] + }; + } + + assert_ast!( + "type Option = None | Some(T)", + vec![decl(TypeDecl { + name: TypeSingletonName { + name: rc("Option"), + params: vec![TypeIdentifier::Singleton(TypeSingletonName { name: rc("T"), params: vec![] })] + }, + mutable: false, + body: TypeBody(vec![ + Variant { id: Default::default(), name: rc("None"), kind: VariantKind::UnitStruct }, + Variant { + id: Default::default(), + name: rc("Some"), + kind: VariantKind::TupleStruct(vec![TypeIdentifier::Singleton(TypeSingletonName { + name: rc("T"), + params: vec![] + })]) + }, + ]) + })] + ); + + assert_ast!( + "type alias Alpha = Beta", + decl(Declaration::TypeAlias { alias: rc("Alpha"), original: rc("Beta") }) + ); + + assert_ast!("type Complex = Unit | Record { field: AnotherType, field2: (Nat, Int), field3: T } | Tuple(Int, (String, T))", + decl(TypeDecl { + name: TypeSingletonName { name: rc("Complex"), params: vec![ + TypeIdentifier::Singleton(TypeSingletonName { name: rc("T"), params: vec![] }), + TypeIdentifier::Singleton(TypeSingletonName { name: rc("U"), params: vec![] }), + ] }, + mutable: false, + body: TypeBody(vec![ + Variant { id: Default::default(), name: rc("Unit"), kind: VariantKind::UnitStruct }, + Variant { id: Default::default(), name: rc("Record"), kind: VariantKind::Record( + vec![ + (rc("field"), TypeIdentifier::Singleton(TypeSingletonName { name: rc("AnotherType"), params: vec![TypeIdentifier::Singleton(TypeSingletonName { name: rc("Bool"), params: vec![] })] })), + (rc("field2"), TypeIdentifier::Tuple(vec![ + TypeIdentifier::Singleton(TypeSingletonName { name: rc("Nat"), params: vec![] }), + TypeIdentifier::Singleton(TypeSingletonName { name: rc("Int"), params: vec![] }), + ] + )), + (rc("field3"), TypeIdentifier::Singleton(TypeSingletonName { name: rc("T"), params: vec![] })), + ] + )}, + Variant { id: Default::default(), name: rc("Tuple"), kind: VariantKind::TupleStruct( + vec![ + TypeIdentifier::Singleton(TypeSingletonName { name: rc("Int"), params: vec![] }), + TypeIdentifier::Tuple(vec![ + TypeIdentifier::Singleton(TypeSingletonName { name: rc("String"), params: vec![] }), + TypeIdentifier::Singleton(TypeSingletonName { name: rc("T"), params: vec![] }), + ]) + ] + )}, + ]), + })); +} + +#[test] +fn declarations() { + use ExpressionKind::*; + + assert_ast!( + "let q_q = Yolo::Swaggins", + vec![decl(Declaration::Binding { + name: rc("q_q"), + constant: true, + type_anno: None, + expr: expr(Value(qn!(Yolo, Swaggins))) + })] + ); +} + +#[test] +fn bindings() { + use ExpressionKind::*; + + assert_ast!( + "let mut a = 10", + vec![decl(Declaration::Binding { + name: rc("a"), + constant: false, + type_anno: None, + expr: expr(NatLiteral(10)), + })] + ); + + assert_ast!( + "let a = 2 + a", + vec![stmt(StatementKind::Declaration(Declaration::Binding { + name: rc("a"), + constant: true, + type_anno: None, + expr: binop("+", expr(NatLiteral(2)), expr(Value(qn!(a)))), + }))] + ); + + assert_ast!( + "let a: Nat = 2", + vec![stmt(StatementKind::Declaration(Declaration::Binding { + name: rc("a"), + constant: true, + type_anno: Some(TypeIdentifier::Singleton(TypeSingletonName { name: rc("Nat"), params: vec![] })), + expr: expr(NatLiteral(2)), + }))] + ); +} + +#[test] +fn functions() { + use ExpressionKind::*; + assert_ast!( + "fn oi()", + vec![stmt(StatementKind::Declaration(Declaration::FuncSig(Signature { + name: rc("oi"), + operator: false, + params: vec![], + type_anno: None + })))] + ); + + assert_ast!( + "oi()", + vec![stmt(StatementKind::Expression(expr(Call { f: bx(expr(Value(qn!(oi)))), arguments: vec![] })))] + ); + + assert_expr!( + "oi(a, 2+2)", + expr(Call { + f: bx(expr(Value(qn!(oi)))), + arguments: vec![ + InvocationArgument::Positional(expr(Value(qn!(a)))), + InvocationArgument::Positional(binop("+", expr(NatLiteral(2)), expr(NatLiteral(2)))), + ] + }) + ); + assert_fail!("a(b,,c)", "Expected a literal expression, got Comma"); + + assert_ast!( + "fn a(b, c: Int): Int", + vec![stmt(StatementKind::Declaration(Declaration::FuncSig(Signature { + name: rc("a"), + operator: false, + params: vec![ + FormalParam { name: rc("b"), default: None, anno: None }, + FormalParam { + name: rc("c"), + default: None, + anno: Some(TypeIdentifier::Singleton(TypeSingletonName { + name: rc("Int"), + params: vec![] + })), + }, + ], + type_anno: Some(TypeIdentifier::Singleton(TypeSingletonName { name: rc("Int"), params: vec![] })), + })))] + ); +} + +#[test] +fn functions_with_different_whitespace() { + use ExpressionKind::*; + + let a = "fn a(x) { x() }"; + let b = "fn a(x) {\n x() }"; + let c = r#" + + fn a(x) { + + + x() + + + } + + "#; + + for item in [a, b, c].iter() { + assert_ast!( + item, + vec![fn_decl( + Signature { + name: rc("a"), + operator: false, + type_anno: None, + params: vec![FormalParam { name: rc("x"), default: None, anno: None }] + }, + vec![stmt(StatementKind::Expression(expr(Call { + f: bx(expr(Value(qn!(x)))), + arguments: vec![], + })))] + .into() + )] + ); + } +} + +#[test] +fn functions_with_default_args() { + use ExpressionKind::*; + + assert_ast!( + "fn func(x: Int, y: Int = 4) { }", + vec![fn_decl( + Signature { + name: rc("func"), + operator: false, + type_anno: None, + params: vec![ + FormalParam { name: rc("x"), anno: Some(ty_simple("Int")), default: None }, + FormalParam { + name: rc("y"), + anno: Some(ty_simple("Int")), + default: Some(expr(NatLiteral(4))) + }, + ], + }, + vec![].into() + )] + ); +} + +#[test] +fn interface() { + let glue = TypeIdentifier::Singleton(TypeSingletonName { name: rc("Glue"), params: vec![] }); + assert_ast!( + "interface Unglueable { fn unglue(a: Glue); fn mar(): Glue }", + vec![decl(Declaration::Interface { + name: rc("Unglueable"), + signatures: vec![ + Signature { + name: rc("unglue"), + operator: false, + params: vec![FormalParam { name: rc("a"), default: None, anno: Some(glue.clone()) },], + type_anno: None, + }, + Signature { name: rc("mar"), operator: false, params: vec![], type_anno: Some(glue) }, + ], + })] + ); +} + +#[test] +fn impls() { + use Declaration::{FuncSig, Impl}; + + assert_ast!( + "impl Heh { fn yolo(); fn swagg(); }", + vec![decl(Impl { + type_name: ty_simple("Heh"), + interface_name: None, + block: vec![ + FuncSig(Signature { name: rc("yolo"), operator: false, params: vec![], type_anno: None }), + FuncSig(Signature { name: rc("swagg"), operator: false, params: vec![], type_anno: None }) + ] + })] + ); + + assert_ast!( + "impl Heh { fn yolo(); fn swagg(); }", + vec![decl(Impl { + type_name: TypeIdentifier::Singleton(TypeSingletonName { + name: rc("Heh"), + params: vec![ty_simple("X")] + }), + interface_name: None, + block: vec![ + FuncSig(Signature { name: rc("yolo"), operator: false, params: vec![], type_anno: None }), + FuncSig(Signature { name: rc("swagg"), operator: false, params: vec![], type_anno: None }) + ] + })] + ); + + assert_ast!( + "impl Heh for Saraz { fn yolo(); fn swagg(); }", + vec![decl(Impl { + type_name: ty_simple("Saraz"), + interface_name: Some(TypeSingletonName { name: rc("Heh"), params: vec![] }), + block: vec![ + FuncSig(Signature { name: rc("yolo"), operator: false, params: vec![], type_anno: None }), + FuncSig(Signature { name: rc("swagg"), operator: false, params: vec![], type_anno: None }) + ] + })] + ); + + assert_ast!( + "impl Heh for (Int, Codepoint) {}", + vec![decl(Impl { + type_name: TypeIdentifier::Tuple(vec![ty_simple("Int"), ty_simple("Codepoint")]), + interface_name: Some(TypeSingletonName { name: rc("Heh"), params: vec![ty_simple("T")] }), + block: vec![] + })] + ); +} + +#[test] +fn annotations() { + use ExpressionKind::*; + + assert_ast! { + r#" + @test_annotation + fn some_function() { + + }"#, + vec![decl(Declaration::Annotation { + name: rc("test_annotation"), + arguments: vec![] + }), + fn_decl(Signature { name: rc("some_function"), operator: false, params: vec![], type_anno: None }, + vec![].into()) + ] + }; + + assert_ast! { + r#" + @test_annotation(some,value) + fn some_function() { + + }"#, + vec![decl(Declaration::Annotation { + name: rc("test_annotation"), + arguments: vec![expr(Value(qn!(some))), expr(Value(qn!(value)))] + }), + fn_decl(Signature { name: rc("some_function"), operator: false, params: vec![], type_anno: None }, + vec![].into()) + ] + }; +} + +#[test] +fn modules() { + assert_ast! { + r#" + module ephraim { + let mut a = 10 + fn nah() { 33 } + } + "#, + vec![stmt(StatementKind::Module(ModuleSpecifier { + name: rc("ephraim"), + contents: vec![ + decl(Declaration::Binding { + name: rc("a"), constant: false, type_anno: None, + expr: expr(ExpressionKind::NatLiteral(10)) + }), + fn_decl(Signature { name: rc("nah"), operator: false, params: vec![], type_anno: None }, + vec![stmt(StatementKind::Expression(expr(ExpressionKind::NatLiteral(33))))].into()), + ].into() + }))] + }; +} + +#[test] +fn imports() { + assert_ast! { + "import harbinger::draughts::Norgleheim", + vec![stmt(StatementKind::Import(ImportSpecifier { + id: ItemId::default(), + path_components: vec![rc("harbinger"), rc("draughts"), rc("Norgleheim")], + imported_names: ImportedNames::LastOfPath + }))] + }; + + assert_ast! { + "import harbinger::draughts::{Norgleheim, Xraksenlaigar}", + vec![stmt(StatementKind::Import(ImportSpecifier { + id: ItemId::default(), + path_components: vec![rc("harbinger"), rc("draughts")], + imported_names: ImportedNames::List(vec![ + rc("Norgleheim"), rc("Xraksenlaigar")]) + }))] + }; + + assert_ast! { + "import bespouri::{}", + vec![stmt(StatementKind::Import(ImportSpecifier { + id: Default::default(), + path_components: vec![rc("bespouri")], + imported_names: ImportedNames::List(vec![]), + }))] + }; + + assert_ast! { + "import bespouri::*", + vec![stmt(StatementKind::Import(ImportSpecifier { + id: Default::default(), + path_components: vec![rc("bespouri")], + imported_names: ImportedNames::All, + }))] + }; +} + +#[test] +fn if_exprs() { + use ExpressionKind::*; + assert_expr!( + "if a() then { tuah(); }", + expr(IfExpression { + discriminator: Some(bx(expr(Call { f: bx(expr(Value(qn!(a)))), arguments: vec![] }))), + body: bx(IfExpressionBody::SimpleConditional { + then_case: vec![exst(Call { f: bx(expr(Value(qn!(tuah)))), arguments: vec![] })].into(), + else_case: None, + }) + }) + ); + + assert_expr!( + "if a then b else c", + expr(IfExpression { + discriminator: Some(bx(expr(Value(qn!(a))))), + body: bx(IfExpressionBody::SimpleConditional { + then_case: vec![exst(Value(qn!(b)))].into(), + else_case: Some(vec![exst(Value(qn!(c)))].into()), + }) + }) + ); + + assert_expr!( + r#" if true then { - const a = 10 + let a = 10 b } else { c }"#, - AST(vec![exst!(IfExpression(bx!(ex!(BoolLiteral(true))), - vec![decl!(Binding { name: rc!(a), constant: true, expr: ex!(NatLiteral(10)) }), - exst!(val!(rc!(b)))], - Some(vec![exst!(val!(rc!(c)))])))]) - ); - - parse_test!("if a { b } else { c }", AST(vec![exst!( - IfExpression(bx!(ex!(val!("a"))), - vec![exst!(val!("b"))], - Some(vec![exst!(val!("c"))])))])); - - parse_test!("if (A {a: 1}) { b } else { c }", AST(vec![exst!( - IfExpression(bx!(ex!(NamedStruct { name: rc!(A), fields: vec![(rc!(a), ex!(NatLiteral(1)))]})), - vec![exst!(val!("b"))], - Some(vec![exst!(val!("c"))])))])); - - parse_error!("if A {a: 1} { b } else { c }"); - */ + expr(IfExpression { + discriminator: Some(bx(expr(BoolLiteral(true)))), + body: bx(IfExpressionBody::SimpleConditional { + then_case: vec![ + decl(Declaration::Binding { + name: rc("a"), + constant: true, + type_anno: None, + expr: expr(NatLiteral(10)) + }), + exst(Value(qn!(b))), + ] + .into(), + else_case: Some(vec![exst(Value(qn!(c))),].into()) + }) + }) + ); } #[test] -fn parsing_impls() { +fn pattern_matching() { + use ExpressionKind::*; + + for item in ["if x is Some(a) then { 4 } else { 9 }", "if x is Some(a) then 4 else 9"] { + assert_expr!( + item, + expr(IfExpression { + discriminator: Some(bx(expr(Value(qn!(x))))), + body: bx(IfExpressionBody::SimplePatternMatch { + pattern: Pattern::TupleStruct(qn!(Some), vec![Pattern::VarOrName(qn!(a))]), + then_case: vec![exst(NatLiteral(4))].into(), + else_case: Some(vec![exst(NatLiteral(9))].into()), + }) + }) + ); + } + + assert_expr!( + "if x is Something { a, b: x } then { 4 } else { 9 }", + expr(IfExpression { + discriminator: Some(bx(expr(Value(qn!(x))))), + body: bx(IfExpressionBody::SimplePatternMatch { + pattern: Pattern::Record( + qn!(Something), + vec![ + (rc("a"), Pattern::Literal(PatternLiteral::StringPattern(rc("a")))), + (rc("b"), Pattern::VarOrName(qn!(x))) + ] + ), + then_case: vec![exst(NatLiteral(4))].into(), + else_case: Some(vec![exst(NatLiteral(9))].into()), + }) + }) + ); + + assert_expr!( + "if x is -1 then 1 else 2", + expr(IfExpression { + discriminator: Some(bx(expr(Value(qn!(x))))), + body: bx(IfExpressionBody::SimplePatternMatch { + pattern: Pattern::Literal(PatternLiteral::NumPattern { neg: true, num: NatLiteral(1) }), + then_case: vec![exst(NatLiteral(1))].into(), + else_case: Some(vec![exst(NatLiteral(2))].into()), + }) + }) + ); + + assert_expr!( + "if x is true then 1 else 2", + expr(IfExpression { + discriminator: Some(bx(expr(Value(qn!(x))))), + body: bx(IfExpressionBody::SimplePatternMatch { + pattern: Pattern::Literal(PatternLiteral::BoolPattern(true)), + then_case: vec![exst(NatLiteral(1))].into(), + else_case: Some(vec![exst(NatLiteral(2))].into()), + }) + }) + ); + + assert_expr!( + "if x { is 1 then 5, else 20 }", + expr(IfExpression { + discriminator: Some(bx(expr(Value(qn!(x))))), + body: bx(IfExpressionBody::CondList(vec![ + ConditionArm { + condition: Condition::Pattern(Pattern::Literal(PatternLiteral::NumPattern { + neg: false, + num: NatLiteral(1) + })), + guard: None, + body: vec![exst(NatLiteral(5))].into(), + }, + ConditionArm { + condition: Condition::Else, + guard: None, + body: vec![exst(NatLiteral(20))].into(), + }, + ])) + }) + ); + + assert_expr!( + r#"if x is "gnosticism" then 1 else 2"#, + expr(IfExpression { + discriminator: Some(bx(expr(Value(qn!(x))))), + body: bx(IfExpressionBody::SimplePatternMatch { + pattern: Pattern::Literal(PatternLiteral::StringPattern(rc("gnosticism"))), + then_case: vec![exst(NatLiteral(1))].into(), + else_case: Some(vec![exst(NatLiteral(2))].into()), + }) + }) + ); + + assert_expr! { + r#" +if (45, "panda", false, 2.2) { + is (49, "pablo", _, 28.4) then "no" + is (_, "panda", _, -2.2) then "yes" + is _ then "maybe" +}"#, + expr( + IfExpression { + discriminator: Some(bx(expr(TupleLiteral(vec![ + expr(NatLiteral(45)), expr(StringLiteral(rc("panda"))), expr(BoolLiteral(false)), expr(FloatLiteral(2.2)) + ])))), + body: bx(IfExpressionBody::CondList(vec![ + ConditionArm { + condition: Condition::Pattern(Pattern::TuplePattern( + vec![ + Pattern::Literal(PatternLiteral::NumPattern { neg: false, num: NatLiteral(49) }), + Pattern::Literal(PatternLiteral::StringPattern(rc("pablo"))), + Pattern::Ignored, + Pattern::Literal(PatternLiteral::NumPattern { neg: false, num: FloatLiteral(28.4) }), + ] + )), + guard: None, + body: vec![stmt(StatementKind::Expression(expr(StringLiteral(rc("no")))))].into(), + }, + ConditionArm { + condition: Condition::Pattern(Pattern::TuplePattern( + vec![ + Pattern::Ignored, + Pattern::Literal(PatternLiteral::StringPattern(rc!(panda))), + Pattern::Ignored, + Pattern::Literal(PatternLiteral::NumPattern { neg: true, num: FloatLiteral(2.2) }), + ] + )), + guard: None, + body: vec![stmt(StatementKind::Expression(expr(StringLiteral(rc("yes")))))].into(), + }, + ConditionArm { + condition: Condition::Pattern(Pattern::Ignored), + guard: None, + body: vec![exst(StringLiteral(rc("maybe")))].into(), + }, + ])) + } + ) + }; }