#![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::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 strlit(s: &str) -> ExpressionKind { ExpressionKind::StringLiteral { s: Rc::new(s.to_string()), prefix: None } } 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 = parser.parse_comb($input); let ast2 = parser.parse_peg($input); let expected = AST { id: Default::default(), statements: $statements.into() }; let ast = match ast { Err(err) => { println!("Parse error: {}", err.msg); panic!(); } Ok(ast) => ast, }; assert_eq!(ast, ast2.unwrap()); assert_eq!(ast, expected); }; } macro_rules! assert_fail { ($input:expr) => { let mut parser = Parser::new(); let _err = parser.parse_comb($input).unwrap_err(); }; ($input:expr, $failure:expr) => { let mut parser = Parser::new(); let err = parser.parse_comb($input).unwrap_err(); println!("assert_fail: {}", err.msg); assert_eq!(err.msg, $failure); }; } macro_rules! assert_expr { ($input:expr, $correct:expr) => { let mut parser = Parser::new(); let expr = parser.expression_comb($input.trim_start()); let expr2 = parser.expression($input.trim_start()); let expr = match expr { Err(err) => { println!("Expression parse error: {}", err.msg); panic!(); } Ok(expr) => expr, }; assert_eq!(expr, expr2.unwrap()); assert_eq!(expr, $correct); }; } macro_rules! assert_fail_expr { ($input:expr, $failure:expr) => { let mut parser = Parser::new(); let _err = parser.expression_comb($input).unwrap_err(); //TODO make real tests for failures //assert_eq!(err.to_string(), $failure); }; } macro_rules! assert_block { ($input:expr, $correct:expr) => { let mut parser = Parser::new(); let block = parser.block_comb($input); let block2 = parser.block($input); let block = match block { Err(err) => { println!("Expression parse error: {}", err.msg); panic!(); } Ok(item) => item, }; assert_eq!(block, block2.unwrap()); assert_eq!(block, $correct); }; } #[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(strlit(""))); assert_expr!(r#""hello""#, expr(strlit("hello"))); assert_expr!( r#"b"some bytestring""#, expr(StringLiteral { s: rc("some bytestring"), prefix: Some(rc("b")) }) ); assert_expr!(r#""Do \n \" escapes work\t""#, expr(strlit("Do \n \" escapes work\t"))); assert_expr!(r#""Georgian letter jani \u{10ef}""#, expr(strlit("Georgian letter jani ჯ"))); } #[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_expr!("[1, /*no*/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(strlit("hella")), expr(NatLiteral(34))]))); assert_expr!( r#"(1+2, "slough")"#, expr(TupleLiteral(vec![ binop("+", expr(NatLiteral(1)), expr(NatLiteral(2))), expr(strlit("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_comb!("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 { identifier: rc("a"), assignment: false, 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 { identifier: rc("n"), assignment: false, 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(strlit("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() { //TODO assert a good error message for this assert_fail!("module::item::call()"); assert_expr!("modulek::item", expr(ExpressionKind::Value(qn!(modulek, item)))); } #[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 { id: Default::default(), fields: 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)))), ] }) ); let err_msg = "0: at line 1, in Alpha:\na(b,,c)\n ^\n\n1: at line 1, in Alt:\na(b,,c)\n ^\n\n2: at line 1, in token:\na(b,,c)\n ^\n\n3: at line 1, in identifier-expr:\na(b,,c)\n ^\n\n4: at line 1, in Alt:\na(b,,c)\n ^\n\n5: at line 1, in primary-expr:\na(b,,c)\n ^\n\n6: at line 1, in extended-expr:\na(b,,c)\n ^\n\n7: at line 1, in prefix-expr:\na(b,,c)\n ^\n\n8: at line 1, in expression-kind:\na(b,,c)\n ^\n\n9: at line 1, in Alt:\na(b,,c)\n ^\n\n10: at line 1, in invocation-argument:\na(b,,c)\n ^\n\n11: at line 1, in call-part:\na(b,,c)\n ^\n\n12: at line 1, in extended-expr:\na(b,,c)\n^\n\n13: at line 1, in prefix-expr:\na(b,,c)\n^\n\n14: at line 1, in expression-kind:\na(b,,c)\n^\n\n15: at line 1, in Parsing-statement:\na(b,,c)\n^\n\n16: at line 1, in AST:\na(b,,c)\n^\n\n"; assert_fail!("a(b,,c)", err_msg); 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 custom_operator() { let source = "fn (!!)(lhs,rhs)"; assert_ast!( source, vec![stmt(StatementKind::Declaration(Declaration::FuncSig(Signature { name: rc("!!"), operator: true, params: vec![ FormalParam { name: rc("lhs"), default: None, anno: None }, FormalParam { name: rc("rhs"), default: None, anno: None }, ], type_anno: None })))] ); } #[test] fn max_function_params() { let mut buf = "fn longfunc(".to_string(); for n in 0..255 { write!(buf, "a{}, ", n).unwrap(); } write!(buf, " a256").unwrap(); write!(buf, ") {{ return 20 }}").unwrap(); //TODO need to create a good, custom error message for this case //assert_fail!(&buf, "A function cannot have more than 255 arguments"); assert_fail!(&buf); let mut buf = r#"\("#.to_string(); for n in 0..255 { write!(buf, "a{}, ", n).unwrap(); } write!(buf, " a256").unwrap(); write!(buf, ") {{ return 10 }}").unwrap(); assert_fail!(&buf); } #[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() })] ); 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_fail!("import bespouri::{}"); 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, }) }) ); //TODO add tests for named expressions 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(strlit("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(strlit("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(strlit("yes"))))].into(), }, ConditionArm { condition: Condition::Pattern(Pattern::Ignored), guard: None, body: vec![exst(strlit("maybe"))].into(), }, ])) } ) }; } #[test] fn flow_control() { use ExpressionKind::*; // This is an incorrect program, but should 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() { assert_block!(case, vec![exst(Value(qn!(a)))].into()); } let source = r#"{ fn quah() { fn foo() { } } }"#; assert_block!( source, 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() ); assert_block!("{}", vec![].into()); let source = r#"{ //hella 4_5 //bog 11; /*chutney*/0xf }"#; assert_block!( source, vec![ Statement { id: Default::default(), location: Default::default(), kind: StatementKind::Expression(Expression::new( Default::default(), ExpressionKind::NatLiteral(45) )) }, Statement { id: Default::default(), location: Default::default(), kind: StatementKind::Expression(Expression::new( Default::default(), ExpressionKind::NatLiteral(11) )) }, Statement { id: Default::default(), location: Default::default(), kind: StatementKind::Expression(Expression::new( Default::default(), ExpressionKind::NatLiteral(15) )) }, ] .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))]); let source = " /*yolo*/ barnaby"; assert_ast!(source, exst(ExpressionKind::Value(qn!(barnaby)))); } //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")]); } */