#![cfg(test)] #![allow(clippy::upper_case_acronyms)] #![allow(clippy::vec_init_then_push)] //use test_case::test_case; use std::{fmt::Write, rc::Rc}; use pretty_assertions::assert_eq; use super::{new::schala_parser, 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_ast2 { ($input:expr, $statements:expr) => { let ast = schala_parser::program($input); let expected = AST { id: Default::default(), statements: $statements.into() }; if ast.is_err() { println!("Parse error: {}", ast.unwrap_err()); panic!(); } assert_eq!(ast.unwrap(), expected); }; } macro_rules! assert_fail { ($input:expr, $failure:expr) => { let err = parse($input).unwrap_err(); assert_eq!(err.msg, $failure); }; } macro_rules! assert_fail2 { ($input:expr, $failure:expr) => { let err = schala_parser::program($input).unwrap_err(); assert_eq!(err.to_string(), $failure); }; } macro_rules! assert_expr { ($input:expr, $correct:expr) => { let mut parser = make_parser($input); assert_eq!(parser.expression().unwrap(), $correct); }; } macro_rules! assert_expr2 { ($input:expr, $correct:expr) => { let expr = schala_parser::expression($input); if expr.is_err() { println!("Expression parse error: {}", expr.unwrap_err()); panic!(); } assert_eq!(expr.unwrap(), $correct); }; } macro_rules! assert_fail_expr2 { ($input:expr, $failure:expr) => { let _err = schala_parser::expression($input).unwrap_err(); //TODO make real tests for failures //assert_eq!(err.to_string(), $failure); }; } #[test] fn basic_literals() { use ExpressionKind::*; assert_expr2!(".2", expr(FloatLiteral(0.2))); assert_expr2!("8.1", expr(FloatLiteral(8.1))); assert_expr2!("0b010", expr(NatLiteral(2))); assert_expr2!("0b0_1_0", expr(NatLiteral(2))); assert_expr2!("0xff", expr(NatLiteral(255))); assert_expr2!("0x032f", expr(NatLiteral(815))); assert_expr2!("0xf_f_", expr(NatLiteral(255))); assert_expr2!("false", expr(BoolLiteral(false))); assert_expr2!("true", expr(BoolLiteral(true))); assert_expr2!(r#""hello""#, expr(StringLiteral(rc("hello")))); } #[test] fn list_literals() { use ExpressionKind::*; assert_expr2!("[]", expr(ListLiteral(vec![]))); assert_expr2!("[1,2]", expr(ListLiteral(vec![expr(NatLiteral(1)), expr(NatLiteral(2)),]))); assert_fail_expr2!("[1,,2]", "some failure"); } #[test] fn binexps() { use ExpressionKind::*; use StatementKind::Expression; assert_expr2!("0xf_f_+1", binop("+", expr(NatLiteral(255)), expr(NatLiteral(1)))); assert_ast2!( "3; 4; 4.3", vec![ stmt(Expression(expr(NatLiteral(3)))), stmt(Expression(expr(NatLiteral(4)))), stmt(Expression(expr(FloatLiteral(4.3)))), ] ); assert_expr2!( "1 + 2 * 3", binop("+", expr(NatLiteral(1)), binop("*", expr(NatLiteral(2)), expr(NatLiteral(3)))) ); assert_expr2!( "1 * 2 + 3", binop("+", binop("*", expr(NatLiteral(1)), expr(NatLiteral(2))), expr(NatLiteral(3))) ); assert_expr2!("1 && 2", binop("&&", expr(NatLiteral(1)), expr(NatLiteral(2)))); assert_expr2!( "1 + 2 * 3 + 4", binop( "+", binop("+", expr(NatLiteral(1)), binop("*", expr(NatLiteral(2)), expr(NatLiteral(3)))), expr(NatLiteral(4)) ) ); assert_expr2!( "(1 + 2) * 3", binop("*", binop("+", expr(NatLiteral(1)), expr(NatLiteral(2))), expr(NatLiteral(3))) ); assert_expr2!(".1 + .2", binop("+", expr(FloatLiteral(0.1)), expr(FloatLiteral(0.2)))); assert_expr2!("1 / 2.", binop("/", expr(NatLiteral(1)), expr(FloatLiteral(2.)))); } #[test] fn prefix_exps() { use ExpressionKind::*; assert_expr2!("-3", prefixop("-", expr(NatLiteral(3)))); assert_expr2!("-0.2", prefixop("-", expr(FloatLiteral(0.2)))); assert_expr2!("!3", prefixop("!", expr(NatLiteral(3)))); assert_expr2!("!t", prefixop("!", expr(Value(qn!(t))))); assert_expr2!("a <- -b", binop("<-", expr(Value(qn!(a))), prefixop("-", expr(Value(qn!(b)))))); assert_expr2!("a <--b", binop("<--", expr(Value(qn!(a))), expr(Value(qn!(b))))); } #[test] fn operators() { use ExpressionKind::*; assert_expr2!("a <- 1", binop("<-", expr(Value(qn!(a))), expr(NatLiteral(1)))); assert_expr2!("a || 1", binop("||", expr(Value(qn!(a))), expr(NatLiteral(1)))); assert_expr2!("a <> 1", binop("<>", expr(Value(qn!(a))), expr(NatLiteral(1)))); } #[test] fn accessors() { use ExpressionKind::*; assert_expr2!("a.b", expr(Access { name: rc("b"), expr: bx(expr(Value(qn!(a)))) })); assert_expr2!( "a.b.c", expr(Access { name: rc("c"), expr: bx(expr(Access { name: rc("b"), expr: bx(expr(Value(qn!(a)))) })) }) ); assert_expr2!( "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_expr2!( "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_expr2!("()", expr(TupleLiteral(vec![]))); assert_expr2!( r#"("hella", 34)"#, expr(TupleLiteral(vec![expr(StringLiteral(rc("hella"))), expr(NatLiteral(34))])) ); assert_expr2!( r#"(1+2, "slough")"#, expr(TupleLiteral(vec![ binop("+", expr(NatLiteral(1)), expr(NatLiteral(2))), expr(StringLiteral(rc("slough"))), ])) ); } #[test] fn identifiers() { use ExpressionKind::*; assert_expr2!("a", expr(Value(qn!(a)))); assert_expr2!("some_value", expr(Value(qn!(some_value)))); assert_expr2!("alpha::beta::gamma", expr(Value(qn!(alpha, beta, gamma)))); assert_expr2!("a + b", binop("+", expr(Value(qn!(a))), expr(Value(qn!(b))))); assert_expr2!("None", expr(Value(qn!(None)))); assert_expr2!( "thing::item::call()", expr(Call { f: bx(expr(Value(qn!(thing, item, call)))), arguments: vec![] }) ); } #[test] fn named_struct() { use ExpressionKind::*; assert_expr2!( "Pandas { a: x + y }", expr(NamedStruct { name: qn!(Pandas), fields: vec![(rc("a"), binop("+", expr(Value(qn!(x))), expr(Value(qn!(y)))))] }) ); assert_expr2!( "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_expr2!( "armok[b,c]", expr(Index { indexee: bx(expr(Value(qn!(armok)))), indexers: vec![expr(Value(qn!(b))), expr(Value(qn!(c)))] }) ); assert_expr2!( "a[b,c][1]", expr(Index { indexee: bx(expr(Index { indexee: bx(expr(Value(qn!(a)))), indexers: vec![expr(Value(qn!(b))), expr(Value(qn!(c)))] })), indexers: vec![expr(NatLiteral(1))] }) ); assert_expr2!( "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_expr2!("a()[b]()[d]", d); assert_fail_expr2!("a[]", "Empty index expressions are not allowed"); } #[test] fn while_expression() { use ExpressionKind::*; assert_expr2!("while { }", expr(WhileExpression { condition: None, body: Block::default() })); assert_expr2!( "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_expr2!( "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_expr2!( "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_ast2! { "type Alpha = Alpha", vec![ decl(TypeDecl { name: TypeSingletonName { name: rc("Alpha"), params: vec![] }, mutable: false, body: TypeBody::Variants(vec![ Variant { id: Default::default(), name: rc("Alpha"), kind: VariantKind::UnitStruct } ]) }) ] }; assert_ast2!( "type mut Kuah = Kuah", decl(TypeDecl { name: TypeSingletonName { name: rc("Kuah"), params: vec![] }, mutable: true, body: TypeBody::Variants(vec![Variant { id: Default::default(), name: rc("Kuah"), kind: VariantKind::UnitStruct }]) }) ); assert_ast2! { "type Alpha = Alpha { a: Int, b: Int }", vec![decl(TypeDecl { name: TypeSingletonName { name: rc("Alpha"), params: vec![] }, mutable: false, body: TypeBody::Variants(vec![ Variant { id: Default::default(), name: rc("Alpha"), kind: VariantKind::Record(vec![ (rc("a"), ty_simple("Int")), (rc("b"), ty_simple("Int")) ]) } ]) })] }; assert_ast2! { "type Alpha = { a: Int, b: Int }", vec![decl(TypeDecl { name: TypeSingletonName { name: rc("Alpha"), params: vec![] }, mutable: false, body: TypeBody::ImmediateRecord(Default::default(), vec![ (rc("a"), ty_simple("Int")), (rc("b"), ty_simple("Int")) ]) })] }; assert_ast2!( "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::Variants(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_ast2!( "type alias Alpha = Beta", decl(Declaration::TypeAlias { alias: rc("Alpha"), original: rc("Beta") }) ); assert_ast2!("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::Variants(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_ast2!( "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_ast2!( "let mut a = 10", vec![decl(Declaration::Binding { name: rc("a"), constant: false, type_anno: None, expr: expr(NatLiteral(10)), })] ); assert_ast2!( "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_ast2!( "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_ast2!( "fn oi()", vec![stmt(StatementKind::Declaration(Declaration::FuncSig(Signature { name: rc("oi"), operator: false, params: vec![], type_anno: None })))] ); assert_ast2!( "oi()", vec![stmt(StatementKind::Expression(expr(Call { f: bx(expr(Value(qn!(oi)))), arguments: vec![] })))] ); assert_expr2!( "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_ast2!( "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 max_function_params() { let mut buf = "fn longfunc(".to_string(); for n in 0..256 { write!(buf, "a{}, ", n).unwrap(); } write!(buf, ") {{ return 20 }}").unwrap(); //assert_fail2!(&buf, "A function cannot have more than 255 arguments"); //TODO better errors again assert_fail2!(&buf, "error at 1:1439: expected ['a' ..= 'z' | 'A' ..= 'Z' | '_']"); } #[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::*; let func = decl(Declaration::FuncDecl( Signature { name: rc("some_function"), operator: false, params: vec![], type_anno: None }, vec![].into(), )); assert_ast! { r#" @test_annotation fn some_function() { }"#, vec![decl(Declaration::Annotation { name: rc("test_annotation"), arguments: vec![], inner: bx(func.clone()), }), ] }; assert_ast! { r#" @test_annotation(some,value) @another_annotation fn some_function() { }"#, vec![decl(Declaration::Annotation { name: rc("test_annotation"), arguments: vec![expr(Value(qn!(some))), expr(Value(qn!(value)))], inner: bx(decl(Declaration::Annotation { name: rc("another_annotation"), arguments: vec![], inner: bx(func) })) }), ] }; } #[test] fn modules() { assert_ast! { r#" module ephraim { let mut a = 10 fn nah() { 33 } } "#, vec![stmt(StatementKind::Declaration(Declaration::Module { name: rc("ephraim"), items: 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_expr2!( "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_expr2!( 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_expr2!( "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::VarOrName(qn!(a))), (rc("b"), Pattern::VarOrName(qn!(x)))] ), then_case: vec![exst(NatLiteral(4))].into(), else_case: Some(vec![exst(NatLiteral(9))].into()), }) }) ); assert_expr2!( "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_expr2!( "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_expr2!( "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_expr2!( 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(), }, ])) } ) }; } #[test] fn flow_control() { use ExpressionKind::*; // This is an incorrect program, but shoudl parse correctly. let source = r#" fn test() { let a = 10; break; continue; return; return 10; }"#; assert_ast!( source, vec![fn_decl( Signature { name: rc("test"), 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::Flow(FlowControl::Break)), stmt(StatementKind::Flow(FlowControl::Continue)), stmt(StatementKind::Flow(FlowControl::Return(None))), stmt(StatementKind::Flow(FlowControl::Return(Some(expr(NatLiteral(10)))))), ] .into() )] ); } #[test] fn blocks() { use ExpressionKind::*; let cases = ["{ a }", "{ a; }", "{a}", "{ a\n }", "{ a\n\n }", "{ a;\n\n; }"]; for case in cases.iter() { let block = schala_parser::block(case); assert_eq!(block.unwrap(), vec![exst(Value(qn!(a)))].into()); } }