From e7308485df4c069e0f49e190a71f00f92d97e0f0 Mon Sep 17 00:00:00 2001 From: Greg Shuflin Date: Sat, 30 Oct 2021 00:15:01 -0700 Subject: [PATCH] Rewrite many parser tests Also introduces pretty print crate for parsing tests. --- Cargo.lock | 47 ++ schala-lang/language/Cargo.toml | 1 + schala-lang/language/src/ast/mod.rs | 5 - schala-lang/language/src/parsing/mod.rs | 8 +- schala-lang/language/src/parsing/new_tests.rs | 670 ++++++++++++++++++ schala-lang/language/src/parsing/test.rs | 417 ----------- 6 files changed, 724 insertions(+), 424 deletions(-) create mode 100644 schala-lang/language/src/parsing/new_tests.rs diff --git a/Cargo.lock b/Cargo.lock index 5b0782e..3b71da9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,6 +20,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi 0.3.8", +] + [[package]] name = "approx" version = "0.1.1" @@ -171,6 +180,16 @@ dependencies = [ "lazy_static 1.4.0", ] +[[package]] +name = "ctor" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccc0a48a9b826acdf4028595adc9db92caea352f7af011a3034acd172a52a0aa" +dependencies = [ + "quote 1.0.10", + "syn 1.0.80", +] + [[package]] name = "derivative" version = "1.0.3" @@ -182,6 +201,12 @@ dependencies = [ "syn 0.15.44", ] +[[package]] +name = "diff" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499" + [[package]] name = "dirs" version = "1.0.5" @@ -524,6 +549,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "output_vt100" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9" +dependencies = [ + "winapi 0.3.8", +] + [[package]] name = "phf" version = "0.7.24" @@ -562,6 +596,18 @@ dependencies = [ "siphasher", ] +[[package]] +name = "pretty_assertions" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0cfe1b2403f172ba0f234e500906ee0a3e493fb81092dac23ebefe129301cc" +dependencies = [ + "ansi_term", + "ctor", + "diff", + "output_vt100", +] + [[package]] name = "proc-macro2" version = "0.4.30" @@ -849,6 +895,7 @@ dependencies = [ "ena", "failure", "itertools", + "pretty_assertions", "radix_trie", "schala-lang-codegen", "schala-repl", diff --git a/schala-lang/language/Cargo.toml b/schala-lang/language/Cargo.toml index 8363f70..b11b3d1 100644 --- a/schala-lang/language/Cargo.toml +++ b/schala-lang/language/Cargo.toml @@ -20,3 +20,4 @@ schala-repl = { path = "../../schala-repl" } [dev-dependencies] test-case = "1.2.0" +pretty_assertions = "1.0.0" diff --git a/schala-lang/language/src/ast/mod.rs b/schala-lang/language/src/ast/mod.rs index d5fa0c4..2d35a81 100644 --- a/schala-lang/language/src/ast/mod.rs +++ b/schala-lang/language/src/ast/mod.rs @@ -165,11 +165,6 @@ impl Expression { pub fn new(id: ItemId, kind: ExpressionKind) -> Expression { Expression { id, kind, type_anno: None } } - - #[cfg(test)] - pub fn with_anno(id: ItemId, kind: ExpressionKind, type_anno: TypeIdentifier) -> Expression { - Expression { id, kind, type_anno: Some(type_anno) } - } } #[derive(Debug, PartialEq, Clone)] diff --git a/schala-lang/language/src/parsing/mod.rs b/schala-lang/language/src/parsing/mod.rs index 5be7ce0..a5b9b50 100644 --- a/schala-lang/language/src/parsing/mod.rs +++ b/schala-lang/language/src/parsing/mod.rs @@ -67,7 +67,7 @@ //! //! ## Expressions //! ```text -//! expression := precedence_expr type_anno+ +//! expression := precedence_expr type_anno? //! precedence_expr := prefix_expr //! prefix_expr := prefix_op call_expr //! prefix_op := "+" | "-" | "!" | "~" @@ -83,7 +83,7 @@ //! //! ```text //! list_expr := "[" (expression, ",")* "]" -//! lambda_expr := "\\" lambda_param_list type_anno+ nonempty_func_body +//! lambda_expr := "\\" lambda_param_list type_anno? nonempty_func_body //! lambda_param_list := formal_param_list | formal_param //! paren_expr := "(" paren_inner ")" //! paren_inner := (expression ",")* @@ -161,6 +161,7 @@ //! module := 'module' IDENTIFIER '{' statement* '}' //! ``` mod test; +mod new_tests; use std::rc::Rc; @@ -729,6 +730,9 @@ impl Parser { let primary = self.primary()?; Ok(if let LSquareBracket = self.token_handler.peek_kind() { let indexers = delimited!(self, LSquareBracket, expression, Comma, RSquareBracket); + if indexers.is_empty() { + return ParseError::new_with_token("Empty index expressions are not allowed", self.token_handler.peek()); + } Expression::new(self.id_store.fresh(), ExpressionKind::Index { indexee: Box::new(Expression::new(self.id_store.fresh(), primary.kind)), indexers, diff --git a/schala-lang/language/src/parsing/new_tests.rs b/schala-lang/language/src/parsing/new_tests.rs new file mode 100644 index 0000000..fa31bdb --- /dev/null +++ b/schala-lang/language/src/parsing/new_tests.rs @@ -0,0 +1,670 @@ +#![cfg(test)] +#![allow(clippy::upper_case_acronyms)] +#![allow(clippy::vec_init_then_push)] +//use test_case::test_case; +use std::rc::Rc; + +use pretty_assertions::assert_eq; + +use super::{tokenize, ParseResult, Parser}; +use crate::{ast::*, tokenizing::Location}; + +fn rc(s: &str) -> Rc { + Rc::new(s.to_owned()) +} + +fn bx(item: T) -> Box { + Box::new(item) +} + +fn make_parser(input: &str) -> Parser { + let tokens: Vec = tokenize(input); + let mut parser = super::Parser::new(); + parser.add_new_tokens(tokens); + parser +} + +fn parse(input: &str) -> ParseResult { + let mut parser = make_parser(input); + parser.parse() +} + +fn stmt(kind: StatementKind) -> Statement { + Statement { location: Location::default(), id: ItemId::default(), kind } +} + +fn 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() } + } + }; +} + +macro_rules! assert_ast { + ($input:expr, $statements:expr) => { + let ast = parse($input).unwrap(); + let expected = AST { id: Default::default(), statements: $statements.into() }; + println!("Expected: {}", expected); + println!("Actual: {}", ast); + assert_eq!(ast, expected); + }; +} + +macro_rules! assert_fail { + ($input:expr, $failure:expr) => { + let err = parse($input).unwrap_err(); + assert_eq!(err.msg, $failure); + }; +} + +macro_rules! assert_expr { + ($input:expr, $correct:expr) => { + let mut parser = make_parser($input); + assert_eq!(parser.expression().unwrap(), $correct); + }; +} + +macro_rules! assert_fail_expr { + ($input:expr, $failure:expr) => { + let mut parser = make_parser($input); + let err = parser.expression().unwrap_err(); + assert_eq!(err.msg, $failure); + }; +} + +#[test] +fn basic_literals() { + use ExpressionKind::*; + + assert_expr!(".2", expr(FloatLiteral(0.2))); + assert_expr!("8.1", expr(FloatLiteral(8.1))); + assert_expr!("0b010", expr(NatLiteral(2))); + assert_expr!("0b0_1_0", expr(NatLiteral(2))); + assert_expr!("0xff", expr(NatLiteral(255))); + assert_expr!("0x032f", expr(NatLiteral(815))); + assert_expr!("0xf_f_", expr(NatLiteral(255))); + assert_expr!("false", expr(BoolLiteral(false))); + assert_expr!("true", expr(BoolLiteral(true))); + assert_expr!(r#""hello""#, expr(StringLiteral(rc("hello")))); +} + +#[test] +fn binexps() { + use ExpressionKind::*; + use StatementKind::Expression; + + assert_expr!("0xf_f_+1", binop("+", expr(NatLiteral(255)), expr(NatLiteral(1)))); + assert_eq!( + parse("3; 4; 4.3").unwrap(), + AST { + id: Default::default(), + statements: vec![ + stmt(Expression(expr(NatLiteral(3)))), + stmt(Expression(expr(NatLiteral(4)))), + stmt(Expression(expr(FloatLiteral(4.3)))), + ] + .into() + } + ); + + assert_expr!( + "1 + 2 * 3", + binop("+", expr(NatLiteral(1)), binop("*", expr(NatLiteral(2)), expr(NatLiteral(3)))) + ); + assert_expr!( + "1 * 2 + 3", + binop("+", binop("*", expr(NatLiteral(1)), expr(NatLiteral(2))), expr(NatLiteral(3))) + ); + assert_expr!("1 && 2", binop("&&", expr(NatLiteral(1)), expr(NatLiteral(2)))); + assert_expr!( + "1 + 2 * 3 + 4", + binop( + "+", + binop("+", expr(NatLiteral(1)), binop("*", expr(NatLiteral(2)), expr(NatLiteral(3)))), + expr(NatLiteral(4)) + ) + ); + assert_expr!( + "(1 + 2) * 3", + binop("*", binop("+", expr(NatLiteral(1)), expr(NatLiteral(2))), expr(NatLiteral(3))) + ); + assert_expr!(".1 + .2", binop("+", expr(FloatLiteral(0.1)), expr(FloatLiteral(0.2)))); + assert_expr!("1 / 2.", binop("/", expr(NatLiteral(1)), expr(FloatLiteral(2.)))); +} + +#[test] +fn prefix_exps() { + use ExpressionKind::*; + + assert_expr!("-3", prefixop("-", expr(NatLiteral(3)))); + assert_expr!("-0.2", prefixop("-", expr(FloatLiteral(0.2)))); + assert_expr!("!3", prefixop("!", expr(NatLiteral(3)))); + assert_expr!("!t", prefixop("!", expr(Value(qn!(t))))); + assert_expr!("a <- -b", binop("<-", expr(Value(qn!(a))), prefixop("-", expr(Value(qn!(b)))))); + assert_expr!("a <--b", binop("<--", expr(Value(qn!(a))), expr(Value(qn!(b))))); +} + +#[test] +fn operators() { + use ExpressionKind::*; + + assert_expr!("a <- 1", binop("<-", expr(Value(qn!(a))), expr(NatLiteral(1)))); + assert_expr!("a || 1", binop("||", expr(Value(qn!(a))), expr(NatLiteral(1)))); + assert_expr!("a <> 1", binop("<>", expr(Value(qn!(a))), expr(NatLiteral(1)))); +} + +//TODO have a test for dot accessors +/* + assert_expr!("a.b.c.d", exst!(binexp!(".", + binexp!(".", + binexp!(".", val!("a"), val!("b")), + val!("c")), + val!("d")))); +*/ + +#[test] +fn tuples() { + use ExpressionKind::*; + + assert_expr!("()", expr(TupleLiteral(vec![]))); + assert_expr!( + r#"("hella", 34)"#, + expr(TupleLiteral(vec![expr(StringLiteral(rc("hella"))), expr(NatLiteral(34))])) + ); + assert_expr!( + r#"(1+2, "slough")"#, + expr(TupleLiteral(vec![ + binop("+", expr(NatLiteral(1)), expr(NatLiteral(2))), + expr(StringLiteral(rc("slough"))), + ])) + ); +} + +#[test] +fn identifiers() { + use ExpressionKind::*; + + assert_expr!("a", expr(Value(qn!(a)))); + assert_expr!("some_value", expr(Value(qn!(some_value)))); + assert_expr!("alpha::beta::gamma", expr(Value(qn!(alpha, beta, gamma)))); + assert_expr!("a + b", binop("+", expr(Value(qn!(a))), expr(Value(qn!(b))))); + assert_expr!("None", expr(Value(qn!(None)))); + assert_expr!( + "thing::item::call()", + expr(Call { f: bx(expr(Value(qn!(thing, item, call)))), arguments: vec![] }) + ); +} + +#[test] +fn named_struct() { + use ExpressionKind::*; + assert_expr!( + "Pandas { a: x + y }", + expr(NamedStruct { + name: qn!(Pandas), + fields: vec![(rc("a"), binop("+", expr(Value(qn!(x))), expr(Value(qn!(y)))))] + }) + ); + assert_expr!( + "Trousers { a:1, b:800 }", + expr(NamedStruct { + name: qn!(Trousers), + fields: vec![(rc("a"), expr(NatLiteral(1))), (rc("b"), expr(NatLiteral(800)))] + }) + ); +} + +#[test] +fn index() { + use ExpressionKind::*; + assert_expr!( + "a[b,c]", + expr(Index { + indexee: bx(expr(Value(qn!(a)))), + indexers: vec![expr(Value(qn!(b))), expr(Value(qn!(c)))] + }) + ); + //TODO this is a parser bug + /* + assert_expr!("perspicacity()[a]", expr(Index{ + indexee: bx(expr(Call { + f: bx(expr(Value(qn!(perspicacity)))), + arguments: vec![] + })), + indexers: vec![expr(Value(qn!(a)))] + })); + */ + //TODO parse_test("a()[b]()[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 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 declarations() { + use ExpressionKind::*; + + assert_ast!( + "let q_q = Yolo::Swaggins", + vec![stmt(StatementKind::Declaration(Declaration::Binding { + name: rc("q_q"), + constant: true, + type_anno: None, + expr: expr(Value(qn!(Yolo, Swaggins))) + }))] + ); +} + +#[test] +fn bindings() { + use ExpressionKind::*; + + assert_ast!( + "let mut a = 10", + vec![decl(Declaration::Binding { + name: rc("a"), + constant: false, + type_anno: None, + expr: expr(NatLiteral(10)), + })] + ); + + assert_ast!( + "let a = 2 + a", + vec![stmt(StatementKind::Declaration(Declaration::Binding { + name: rc("a"), + constant: true, + type_anno: None, + expr: binop("+", expr(NatLiteral(2)), expr(Value(qn!(a)))), + }))] + ); + + assert_ast!( + "let a: Nat = 2", + vec![stmt(StatementKind::Declaration(Declaration::Binding { + name: rc("a"), + constant: true, + type_anno: Some(TypeIdentifier::Singleton(TypeSingletonName { name: rc("Nat"), params: vec![] })), + expr: expr(NatLiteral(2)), + }))] + ); +} + +#[test] +fn functions() { + use ExpressionKind::*; + assert_ast!( + "fn oi()", + vec![stmt(StatementKind::Declaration(Declaration::FuncSig(Signature { + name: rc("oi"), + operator: false, + params: vec![], + type_anno: None + })))] + ); + + assert_ast!( + "oi()", + vec![stmt(StatementKind::Expression(expr(Call { f: bx(expr(Value(qn!(oi)))), arguments: vec![] })))] + ); + + assert_expr!( + "oi(a, 2+2)", + expr(Call { + f: bx(expr(Value(qn!(oi)))), + arguments: vec![ + InvocationArgument::Positional(expr(Value(qn!(a)))), + InvocationArgument::Positional(binop("+", expr(NatLiteral(2)), expr(NatLiteral(2)))), + ] + }) + ); + assert_fail!("a(b,,c)", "Expected a literal expression, got Comma"); + + assert_ast!( + "fn a(b, c: Int): Int", + vec![stmt(StatementKind::Declaration(Declaration::FuncSig(Signature { + name: rc("a"), + operator: false, + params: vec![ + FormalParam { name: rc("b"), default: None, anno: None }, + FormalParam { + name: rc("c"), + default: None, + anno: Some(TypeIdentifier::Singleton(TypeSingletonName { + name: rc("Int"), + params: vec![] + })), + }, + ], + type_anno: Some(TypeIdentifier::Singleton(TypeSingletonName { name: rc("Int"), params: vec![] })), + })))] + ); +} + +#[test] +fn functions_with_different_whitespace() { + use ExpressionKind::*; + + let a = "fn a(x) { x() }"; + let b = "fn a(x) {\n x() }"; + let c = r#" + + fn a(x) { + + + x() + + + } + + "#; + + for item in [a, b, c].iter() { + assert_ast!( + item, + vec![fn_decl( + Signature { + name: rc("a"), + operator: false, + type_anno: None, + params: vec![FormalParam { name: rc("x"), default: None, anno: None }] + }, + vec![stmt(StatementKind::Expression(expr(Call { + f: bx(expr(Value(qn!(x)))), + arguments: vec![], + })))] + .into() + )] + ); + } +} + +#[test] +fn functions_with_default_args() { + use ExpressionKind::*; + let int_anno = TypeIdentifier::Singleton(TypeSingletonName { name: rc("Int"), params: vec![] }); + + 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(int_anno.clone()), default: None }, + FormalParam { name: rc("y"), anno: Some(int_anno), 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 annotations() { + use ExpressionKind::*; + + assert_ast! { + r#" + @test_annotation + fn some_function() { + + }"#, + vec![decl(Declaration::Annotation { + name: rc("test_annotation"), + arguments: vec![] + }), + fn_decl(Signature { name: rc("some_function"), operator: false, params: vec![], type_anno: None }, + vec![].into()) + ] + }; + + assert_ast! { + r#" + @test_annotation(some,value) + fn some_function() { + + }"#, + vec![decl(Declaration::Annotation { + name: rc("test_annotation"), + arguments: vec![expr(Value(qn!(some))), expr(Value(qn!(value)))] + }), + fn_decl(Signature { name: rc("some_function"), operator: false, params: vec![], type_anno: None }, + vec![].into()) + ] + }; +} + +#[test] +fn modules() { + assert_ast! { + r#" + module ephraim { + let mut a = 10 + fn nah() { 33 } + } + "#, + vec![stmt(StatementKind::Module(ModuleSpecifier { + name: rc("ephraim"), + contents: vec![ + decl(Declaration::Binding { + name: rc("a"), constant: false, type_anno: None, + expr: expr(ExpressionKind::NatLiteral(10)) + }), + fn_decl(Signature { name: rc("nah"), operator: false, params: vec![], type_anno: None }, + vec![stmt(StatementKind::Expression(expr(ExpressionKind::NatLiteral(33))))].into()), + ].into() + }))] + }; +} + +#[test] +fn imports() { + assert_ast! { + "import harbinger::draughts::Norgleheim", + vec![stmt(StatementKind::Import(ImportSpecifier { + id: ItemId::default(), + path_components: vec![rc("harbinger"), rc("draughts"), rc("Norgleheim")], + imported_names: ImportedNames::LastOfPath + }))] + }; + + assert_ast! { + "import harbinger::draughts::{Norgleheim, Xraksenlaigar}", + vec![stmt(StatementKind::Import(ImportSpecifier { + id: ItemId::default(), + path_components: vec![rc("harbinger"), rc("draughts")], + imported_names: ImportedNames::List(vec![ + rc("Norgleheim"), rc("Xraksenlaigar")]) + }))] + }; + + assert_ast! { + "import bespouri::{}", + vec![stmt(StatementKind::Import(ImportSpecifier { + id: Default::default(), + path_components: vec![rc("bespouri")], + imported_names: ImportedNames::List(vec![]), + }))] + }; + + assert_ast! { + "import bespouri::*", + vec![stmt(StatementKind::Import(ImportSpecifier { + id: Default::default(), + path_components: vec![rc("bespouri")], + imported_names: ImportedNames::All, + }))] + }; +} diff --git a/schala-lang/language/src/parsing/test.rs b/schala-lang/language/src/parsing/test.rs index 768eddd..ffdd2af 100644 --- a/schala-lang/language/src/parsing/test.rs +++ b/schala-lang/language/src/parsing/test.rs @@ -13,7 +13,6 @@ use super::TypeIdentifier::*; use super::TypeSingletonName; use super::ExpressionKind::*; use super::VariantKind::*; -use super::ForBody::*; fn make_parser(input: &str) -> Parser { let tokens: Vec = tokenize(input); @@ -50,9 +49,6 @@ macro_rules! parse_test { macro_rules! parse_test_wrap_ast { ($string:expr, $correct:expr) => { parse_test!($string, AST { id: Default::default(), statements: vec![$correct].into() }) } } -macro_rules! parse_error { - ($string:expr) => { assert!(parse($string).is_err()) } -} macro_rules! qname { ( $( $component:expr),* ) => { { @@ -80,18 +76,6 @@ macro_rules! decl { }; } -macro_rules! import { - ($import_spec:expr) => { - make_statement(StatementKind::Import($import_spec)) - } -} - -macro_rules! module { - ($module_spec:expr) => { - make_statement(StatementKind::Module($module_spec)) - } -} - macro_rules! ex { ($expr_type:expr) => { Expression::new(Default::default(), $expr_type) }; ($expr_type:expr, $type_anno:expr) => { Expression::with_anno(Default::default(), $expr_type, $type_anno) }; @@ -107,12 +91,6 @@ macro_rules! inv { ($expr_type:expr) => { InvocationArgument::Positional($expr_type) } } -macro_rules! binexp { - ($op:expr, $lhs:expr, $rhs:expr) => { BinExp(BinOp::from_sigil($op), bx!(Expression::new(Default::default(), $lhs).into()), bx!(Expression::new(Default::default(), $rhs).into())) } -} -macro_rules! prefexp { - ($op:expr, $lhs:expr) => { PrefixExp(PrefixOp::from_sigil($op), bx!(Expression::new(Default::default(), $lhs).into())) } -} macro_rules! exst { ($expr_type:expr) => { make_statement(StatementKind::Expression(Expression::new(Default::default(), $expr_type).into())) }; ($expr_type:expr, $type_anno:expr) => { make_statement(StatementKind::Expression(Expression::with_anno(Default::default(), $expr_type, $type_anno).into())) }; @@ -125,201 +103,6 @@ macro_rules! exst { } } -#[test] -fn parsing_number_literals_and_binexps() { - parse_test_wrap_ast! { ".2", exst!(FloatLiteral(0.2)) }; - parse_test_wrap_ast! { "8.1", exst!(FloatLiteral(8.1)) }; - - parse_test_wrap_ast! { "0b010", exst!(NatLiteral(2)) }; - parse_test_wrap_ast! { "0b0_1_0_", exst!(NatLiteral(2)) } - - parse_test_wrap_ast! {"0xff", exst!(NatLiteral(255)) }; - parse_test_wrap_ast! {"0xf_f_", exst!(NatLiteral(255)) }; - - parse_test_wrap_ast! {"0xf_f_+1", exst!(binexp!("+", NatLiteral(255), NatLiteral(1))) }; - - parse_test! {"3; 4; 4.3", - AST { - id: Default::default(), - statements: vec![exst!(NatLiteral(3)), exst!(NatLiteral(4)), - exst!(FloatLiteral(4.3))].into() - } - }; - - parse_test_wrap_ast!("1 + 2 * 3", - exst!(binexp!("+", NatLiteral(1), binexp!("*", NatLiteral(2), NatLiteral(3)))) - ); - - parse_test_wrap_ast!("1 * 2 + 3", - exst!(binexp!("+", binexp!("*", NatLiteral(1), NatLiteral(2)), NatLiteral(3))) - ) ; - - parse_test_wrap_ast!("1 && 2", exst!(binexp!("&&", NatLiteral(1), NatLiteral(2)))); - - parse_test_wrap_ast!("1 + 2 * 3 + 4", exst!( - binexp!("+", - binexp!("+", NatLiteral(1), binexp!("*", NatLiteral(2), NatLiteral(3))), - NatLiteral(4)))); - - parse_test_wrap_ast!("(1 + 2) * 3", - exst!(binexp!("*", binexp!("+", NatLiteral(1), NatLiteral(2)), NatLiteral(3)))); - - parse_test_wrap_ast!(".1 + .2", exst!(binexp!("+", FloatLiteral(0.1), FloatLiteral(0.2)))); - parse_test_wrap_ast!("1 / 2", exst!(binexp!("/", NatLiteral(1), NatLiteral(2)))); -} - -#[test] -fn parsing_tuples() { - parse_test_wrap_ast!("()", exst!(TupleLiteral(vec![]))); - parse_test_wrap_ast!("(\"hella\", 34)", exst!( - TupleLiteral( - vec![ex!(s r#""hella""#), ex!(s "34")] - ) - )); - parse_test_wrap_ast!("((1+2), \"slough\")", exst!(TupleLiteral(vec![ - ex!(binexp!("+", NatLiteral(1), NatLiteral(2))), - ex!(StringLiteral(rc!(slough))), - ]))) -} - -#[test] -fn parsing_identifiers() { - parse_test_wrap_ast!("a", exst!(val!("a"))); - parse_test_wrap_ast!("some_value", exst!(val!("some_value"))); - parse_test_wrap_ast!("a + b", exst!(binexp!("+", val!("a"), val!("b")))); - //parse_test!("a[b]", AST(vec![Expression( - //parse_test!("a[]", <- TODO THIS NEEDS TO FAIL - //parse_test("a()[b]()[d]") - //TODO fix this parsing stuff - /* - parse_test! { "perspicacity()[a]", AST(vec![ - exst!(Index { - indexee: bx!(ex!(Call { f: bx!(ex!(val!("perspicacity"))), arguments: vec![] })), - indexers: vec![ex!(val!("a"))] - }) - ]) - } - */ - parse_test_wrap_ast!("a[b,c]", exst!(Index { indexee: bx!(ex!(val!("a"))), indexers: vec![ex!(val!("b")), ex!(val!("c"))]} )); - - parse_test_wrap_ast!("None", exst!(val!("None"))); - parse_test_wrap_ast!("Pandas { a: x + y }", - exst!(NamedStruct { name: qname!(Pandas), fields: vec![(rc!(a), ex!(binexp!("+", val!("x"), val!("y"))))]}) - ); - parse_test_wrap_ast! { "Pandas { a: n, b: q, }", - exst!(NamedStruct { name: qname!(Pandas), fields: - vec![(rc!(a), ex!(val!("n"))), (rc!(b), ex!(val!("q")))] - } - ) - }; -} - - -#[test] -fn qualified_identifiers() { - parse_test_wrap_ast! { - "let q_q = Yolo::Swaggins", - decl!(Binding { name: rc!(q_q), constant: true, type_anno: None, - expr: Expression::new(Default::default(), Value(qname!(Yolo, Swaggins))), - }) - } - - parse_test_wrap_ast! { - "thing::item::call()", - exst!(Call { f: bx![ex!(Value(qname!(thing, item, call)))], arguments: vec![] }) - } -} - -#[test] -fn reserved_words() { - parse_error!("module::item::call()"); -} - -#[test] -fn parsing_complicated_operators() { - parse_test_wrap_ast!("a <- b", exst!(binexp!("<-", val!("a"), val!("b")))); - parse_test_wrap_ast!("a || b", exst!(binexp!("||", val!("a"), val!("b")))); - parse_test_wrap_ast!("a<>b", exst!(binexp!("<>", val!("a"), val!("b")))); - parse_test_wrap_ast!("a.b.c.d", exst!(binexp!(".", - binexp!(".", - binexp!(".", val!("a"), val!("b")), - val!("c")), - val!("d")))); - parse_test_wrap_ast!("-3", exst!(prefexp!("-", NatLiteral(3)))); - parse_test_wrap_ast!("-0.2", exst!(prefexp!("-", FloatLiteral(0.2)))); - parse_test_wrap_ast!("!3", exst!(prefexp!("!", NatLiteral(3)))); - parse_test_wrap_ast!("a <- -b", exst!(binexp!("<-", val!("a"), prefexp!("-", val!("b"))))); - parse_test_wrap_ast!("a <--b", exst!(binexp!("<--", val!("a"), val!("b")))); -} - -#[test] -fn parsing_functions() { - parse_test_wrap_ast!("fn oi()", decl!(FuncSig(Signature { name: rc!(oi), operator: false, params: vec![], type_anno: None }))); - parse_test_wrap_ast!("oi()", exst!(Call { f: bx!(ex!(val!("oi"))), arguments: vec![] })); - parse_test_wrap_ast!("oi(a, 2 + 2)", exst!(Call - { f: bx!(ex!(val!("oi"))), - arguments: vec![inv!(ex!(val!("a"))), inv!(ex!(binexp!("+", NatLiteral(2), NatLiteral(2))))] - })); - parse_error!("a(b,,c)"); - - parse_test_wrap_ast!("fn a(b, c: Int): Int", decl!( - FuncSig(Signature { name: rc!(a), operator: false, params: vec![ - FormalParam { name: rc!(b), anno: None, default: None }, - FormalParam { name: rc!(c), anno: Some(ty!("Int")), default: None } - ], type_anno: Some(ty!("Int")) }))); - - - parse_test_wrap_ast!("fn a(x) { x() }", decl!( - FuncDecl(Signature { name: rc!(a), operator: false, params: vec![FormalParam { name: rc!(x), anno: None, default: None }], type_anno: None }, - vec![exst!(Call { f: bx!(ex!(val!("x"))), arguments: vec![] })].into()))); - parse_test_wrap_ast!("fn a(x) {\n x() }", decl!( - FuncDecl(Signature { name: rc!(a), operator: false, params: vec![FormalParam { name: rc!(x), anno: None, default: None }], type_anno: None }, - vec![exst!(Call { f: bx!(ex!(val!("x"))), arguments: vec![] })].into()))); - - let multiline = r#" -fn a(x) { -x() -} -"#; -parse_test_wrap_ast!(multiline, decl!( - FuncDecl(Signature { name: rc!(a), operator: false, params: vec![FormalParam { name: rc!(x), default: None, anno: None }], type_anno: None }, - vec![exst!(Call { f: bx!(ex!(val!("x"))), arguments: vec![] })].into()))); - let multiline2 = r#" -fn a(x) { - -x() - -} -"#; -parse_test_wrap_ast!(multiline2, decl!( - FuncDecl(Signature { name: rc!(a), operator: false, params: vec![FormalParam { name: rc!(x), default: None, anno: None }], type_anno: None }, - exst!(s "x()").into()))); -} - -#[test] -fn functions_with_default_args() { - parse_test_wrap_ast! { - "fn func(x: Int, y: Int = 4) { }", - decl!( - FuncDecl(Signature { name: rc!(func), operator: false, type_anno: None, params: vec![ - FormalParam { name: rc!(x), default: None, anno: Some(ty!("Int")) }, - FormalParam { name: rc!(y), default: Some(ex!(s "4")), anno: Some(ty!("Int")) } - ]}, vec![].into()) - ) - }; -} - -#[test] -fn parsing_bools() { - parse_test_wrap_ast!("false", exst!(BoolLiteral(false))); - parse_test_wrap_ast!("true", exst!(BoolLiteral(true))); -} - -#[test] -fn parsing_strings() { - parse_test_wrap_ast!(r#""hello""#, exst!(StringLiteral(rc!(hello)))); -} - #[test] fn parsing_types() { parse_test_wrap_ast!("type Yolo = Yolo", decl!(TypeDecl { name: tys!("Yolo"), body: TypeBody(vec![Variant { id: ItemId::default(), kind: UnitStruct, name: rc!(Yolo) }]), mutable: false} )); @@ -363,16 +146,6 @@ fn parsing_types() { }; } -#[test] -fn parsing_bindings() { - parse_test_wrap_ast!("let mut a = 10", decl!(Binding { name: rc!(a), constant: false, type_anno: None, expr: ex!(NatLiteral(10)) } )); - parse_test_wrap_ast!("let a = 2 + 2", decl!(Binding { name: rc!(a), constant: true, type_anno: None, expr: ex!(binexp!("+", NatLiteral(2), NatLiteral(2))) })); - parse_test_wrap_ast!("let a: Nat = 2 + 2", decl!( - Binding { name: rc!(a), constant: true, type_anno: Some(Singleton(TypeSingletonName { name: rc!(Nat), params: vec![] })), - expr: ex!(binexp!("+", NatLiteral(2), NatLiteral(2))) } - )); -} - #[test] fn parsing_block_expressions() { parse_test_wrap_ast! { @@ -439,25 +212,6 @@ fn parsing_block_expressions() { parse_error!("if A {a: 1} { b } else { c }"); */ } -#[test] -fn parsing_interfaces() { - parse_test_wrap_ast!("interface Unglueable { fn unglue(a: Glue); fn mar(): Glue }", - decl!(Interface { - name: rc!(Unglueable), - signatures: vec![ - Signature { - name: rc!(unglue), - operator: false, - params: vec![ - FormalParam { name: rc!(a), anno: Some(Singleton(TypeSingletonName { name: rc!(Glue), params: vec![] })), default: None } - ], - type_anno: None - }, - Signature { name: rc!(mar), operator: false, params: vec![], type_anno: Some(Singleton(TypeSingletonName { name: rc!(Glue), params: vec![] })) }, - ] - }) - ); -} #[test] fn parsing_impls() { @@ -497,33 +251,6 @@ fn parsing_impls() { })); } -#[test] -fn parsing_type_annotations() { - parse_test_wrap_ast!("let a = b : Int", - decl!(Binding { name: rc!(a), constant: true, type_anno: None, expr: - ex!(val!("b"), ty!("Int")) })); - - parse_test_wrap_ast!("a : Int", - exst!(val!("a"), ty!("Int")) - ); - - parse_test_wrap_ast!("a : Option", - exst!(val!("a"), Singleton(TypeSingletonName { name: rc!(Option), params: vec![ty!("Int")] })) - ); - - parse_test_wrap_ast!("a : KoreanBBQSpecifier >", - exst!(val!("a"), Singleton(TypeSingletonName { name: rc!(KoreanBBQSpecifier), params: vec![ - ty!("Kimchi"), Singleton(TypeSingletonName { name: rc!(Option), params: vec![ty!("Bulgogi")] }) - ] })) - ); - - parse_test_wrap_ast!("a : (Int, Yolo)", - exst!(val!("a"), Tuple( - vec![ty!("Int"), Singleton(TypeSingletonName { - name: rc!(Yolo), params: vec![ty!("a")] - })]))); -} - #[test] fn parsing_lambdas() { parse_test_wrap_ast! { r#"\(x) { x + 1}"#, exst!( @@ -615,37 +342,6 @@ fn more_advanced_lambdas() { }; } -#[test] -fn while_expr() { - parse_test_wrap_ast! { - "while { }", - exst!(WhileExpression { condition: None, body: Block::default() }) - } - - parse_test_wrap_ast! { - "while a == b { }", - exst!(WhileExpression { condition: Some(bx![ex![binexp!("==", val!("a"), val!("b"))]]), body: Block::default() }) - } -} - -#[test] -fn for_expr() { - parse_test_wrap_ast! { - "for { a <- maybeValue } return 1", - exst!(ForExpression { - enumerators: vec![Enumerator { id: rc!(a), generator: ex!(val!("maybeValue")) }], - body: bx!(MonadicReturn(ex!(s "1"))) - }) - } - - parse_test_wrap_ast! { - "for n <- someRange { f(n); }", - exst!(ForExpression { enumerators: vec![Enumerator { id: rc!(n), generator: ex!(val!("someRange"))}], - body: bx!(ForBody::StatementBlock(vec![exst!(s "f(n)")].into())) - }) - } -} - #[test] fn patterns() { parse_test_wrap_ast! { @@ -751,58 +447,6 @@ fn pattern_literals() { } } -#[test] -fn imports() { - parse_test_wrap_ast! { - "import harbinger::draughts::Norgleheim", - import!(ImportSpecifier { - id: Default::default(), - path_components: vec![rc!(harbinger), rc!(draughts), rc!(Norgleheim)], - imported_names: ImportedNames::LastOfPath - }) - } -} - -#[test] -fn imports_2() { - parse_test_wrap_ast! { - "import harbinger::draughts::{Norgleheim, Xraksenlaigar}", - import!(ImportSpecifier { - id: Default::default(), - path_components: vec![rc!(harbinger), rc!(draughts)], - imported_names: ImportedNames::List(vec![ - rc!(Norgleheim), - rc!(Xraksenlaigar) - ]) - }) - } -} - -#[test] -fn imports_3() { - parse_test_wrap_ast! { - "import bespouri::{}", - import!(ImportSpecifier { - id: Default::default(), - path_components: vec![rc!(bespouri)], - imported_names: ImportedNames::List(vec![]) - }) - } -} - - -#[test] -fn imports_4() { - parse_test_wrap_ast! { - "import bespouri::*", - import!(ImportSpecifier { - id: Default::default(), - path_components: vec![rc!(bespouri)], - imported_names: ImportedNames::All - }) - } -} - #[test] fn if_expr() { @@ -878,64 +522,3 @@ if (45, "panda", false, 2.2) { ) } } - -#[test] -fn modules() { - parse_test_wrap_ast! { -r#" - module ephraim { - let a = 10 - fn nah() { 33 } - } -"#, -module!( - ModuleSpecifier { name: rc!(ephraim), contents: vec![ - decl!(Binding { name: rc!(a), constant: true, type_anno: None, expr: ex!(s "10") }), - decl!(FuncDecl(Signature { name: rc!(nah), operator: false, params: vec![], type_anno: None }, vec![exst!(NatLiteral(33))].into())), - ].into() } -) - } -} - -#[test] -fn annotations() { - parse_test! { - r#" - @test_annotation - fn some_function() { - - }"#, - AST { - id: Default::default(), - statements: vec![ - decl!(Annotation { name: rc!(test_annotation), arguments: vec![] }), - decl!(FuncDecl( - Signature { name: rc!(some_function), operator: false, params: vec![], type_anno: None } - , vec![].into() - ) - ) - ].into() - } - }; - parse_test! { - r#" - @test_annotation(some, value) - fn some_function() { - - }"#, - AST { - id: Default::default(), - statements: vec![ - decl!(Annotation { name: rc!(test_annotation), arguments: vec![ - ex!(val!("some")), - ex!(val!("value")) - ] }), - decl!(FuncDecl( - Signature { name: rc!(some_function), operator: false, params: vec![], type_anno: None } - , vec![].into() - ) - ) - ].into() - } - }; -}