#![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, Parser}; use crate::{ast::*, parsing::Location}; fn rc(s: &str) -> Rc { Rc::new(s.to_owned()) } fn bx(item: T) -> Box { Box::new(item) } 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 mut parser = Parser::new(); let ast = schala_parser::program($input, &mut parser); 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 mut parser = Parser::new(); let err = schala_parser::program($input, &mut parser).unwrap_err(); assert_eq!(err.to_string(), $failure); }; } macro_rules! assert_expr { ($input:expr, $correct:expr) => { let mut parser = Parser::new(); let expr = schala_parser::expression($input, &mut parser); if expr.is_err() { println!("Expression parse error: {}", expr.unwrap_err()); panic!(); } assert_eq!(expr.unwrap(), $correct); }; } macro_rules! assert_fail_expr { ($input:expr, $failure:expr) => { let mut parser = Parser::new(); let _err = schala_parser::expression($input, &mut parser).unwrap_err(); //TODO make real tests for failures //assert_eq!(err.to_string(), $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))); } #[test] fn string_literals() { use ExpressionKind::*; assert_expr!(r#""""#, expr(StringLiteral(rc("")))); assert_expr!(r#""hello""#, expr(StringLiteral(rc("hello")))); assert_expr!(r#"b"some bytestring""#, expr(StringLiteral(rc("some bytestring")))); //NOTE I'm not 100% sure this case is correct, but I'll deal with it later assert_expr!(r#""Do \n \" escapes work\t""#, expr(StringLiteral(rc(r#"Do \n \" escapes work\t"#)))); } #[test] fn list_literals() { use ExpressionKind::*; assert_expr!("[]", expr(ListLiteral(vec![]))); assert_expr!("[1,2]", expr(ListLiteral(vec![expr(NatLiteral(1)), expr(NatLiteral(2)),]))); assert_fail_expr!("[1,,2]", "some failure"); } #[test] fn binexps() { use ExpressionKind::*; use StatementKind::Expression; assert_expr!("0xf_f_+1", binop("+", expr(NatLiteral(255)), expr(NatLiteral(1)))); assert_ast!( "3; 4; 4.3", vec![ stmt(Expression(expr(NatLiteral(3)))), stmt(Expression(expr(NatLiteral(4)))), stmt(Expression(expr(FloatLiteral(4.3)))), ] ); 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!( "armok[b,c]", expr(Index { indexee: bx(expr(Value(qn!(armok)))), indexers: vec![expr(Value(qn!(b))), expr(Value(qn!(c)))] }) ); assert_expr!( "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_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::*; //TODO support this without the semicolon after the lambda 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()", "error at 1:7: expected ['a' ..= 'z' | 'A' ..= 'Z' | '_']"); } #[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::Variants(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::Variants(vec![Variant { id: Default::default(), name: rc("Kuah"), kind: VariantKind::UnitStruct }]) }) ); assert_ast! { "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_ast! { "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_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::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_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::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_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)","error at 1:5: expected one of \"(\", \".\", \"0b\", \"0x\", \"[\", \"\\\"\", \"_\", \"false\", \"for\", \"if\", \"true\", \"while\", ['+' | '-' | '!'], ['0' ..= '9'], ['a' ..= 'z' | 'A' ..= 'Z' | '_'], r#\"\\\"#"); 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![] })), })))] ); let source = r#" fn some_function() { }"#; assert_ast!( source, vec![fn_decl( Signature { name: rc("some_function"), operator: false, type_anno: None, params: vec![] }, vec![].into() )] ); } #[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_fail!(&buf, "A function cannot have more than 255 arguments"); //TODO better errors again assert_fail!(&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::{FuncDecl, Impl}; let block = vec![ FuncDecl( Signature { name: rc("yolo"), operator: false, params: vec![], type_anno: None }, vec![].into(), ), FuncDecl( Signature { name: rc("swagg"), operator: false, params: vec![], type_anno: None }, vec![].into(), ), ]; assert_ast!( "impl Heh { fn yolo() { }; fn swagg() { } }", vec![decl(Impl { type_name: ty_simple("Heh"), interface_name: None, block: block.clone() })] ); //TODO `"impl Heh { fn yolo() { }; fn swagg() { }; }"` ought to work 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: block.clone(), })] ); 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: block.clone(), })] ); 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_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::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_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(), }, ])) } ) }; } #[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; }"]; let mut parser = Parser::new(); for case in cases.iter() { let block = schala_parser::block(case, &mut parser); assert_eq!(block.unwrap(), vec![exst(Value(qn!(a)))].into()); } let source = r#"{ fn quah() { fn foo() { } } }"#; let block = schala_parser::block(source, &mut parser); assert_eq!( block.unwrap(), vec![decl(Declaration::FuncDecl( Signature { name: rc("quah"), operator: false, params: vec![], type_anno: None }, vec![decl(Declaration::FuncDecl( Signature { name: rc("foo"), operator: false, params: vec![], type_anno: None }, vec![].into(), ))] .into() ))] .into() ); } #[test] fn comments() { use ExpressionKind::*; let source = "1 + /* hella /* bro */ */ 2"; assert_expr!(source, binop("+", expr(NatLiteral(1)), expr(NatLiteral(2)))); //TODO make sure this error message makes sense let source = "1 + /* hella /* bro */ 2"; assert_fail_expr!(source, "foo"); let source = "1 + /* hella */ bro */ 2"; assert_fail_expr!(source, binop("+", expr(NatLiteral(1)), expr(NatLiteral(2)))); let source = "5//no man\n"; assert_ast!(source, vec![exst(NatLiteral(5))]); } //TODO support backtick operators like this /* #[test] fn backtick_operators() { let output = token_kinds("1 `plus` 2"); assert_eq!(output, vec![digit!("1"), op!("plus"), digit!("2")]); } */