Rewrite many parser tests

Also introduces pretty print crate for parsing tests.
This commit is contained in:
Greg Shuflin 2021-10-30 00:15:01 -07:00
parent 08d66f0a43
commit e7308485df
6 changed files with 724 additions and 424 deletions

47
Cargo.lock generated
View File

@ -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",

View File

@ -20,3 +20,4 @@ schala-repl = { path = "../../schala-repl" }
[dev-dependencies]
test-case = "1.2.0"
pretty_assertions = "1.0.0"

View File

@ -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)]

View File

@ -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,

View File

@ -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<String> {
Rc::new(s.to_owned())
}
fn bx<T>(item: T) -> Box<T> {
Box::new(item)
}
fn make_parser(input: &str) -> Parser {
let tokens: Vec<crate::tokenizing::Token> = tokenize(input);
let mut parser = super::Parser::new();
parser.add_new_tokens(tokens);
parser
}
fn parse(input: &str) -> ParseResult<AST> {
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<Int>",
expr_anno(
Value(qn!(a)),
Singleton(TypeSingletonName {
name: rc("Option"),
params: vec![Singleton(TypeSingletonName { name: rc("Int"), params: vec![] })]
})
)
);
assert_expr!(
"a: KoreanBBQSpecifier<Kimchi, Option<Bulgogi> >",
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<a>)",
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,
}))]
};
}

View File

@ -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<crate::tokenizing::Token> = 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<Int>",
exst!(val!("a"), Singleton(TypeSingletonName { name: rc!(Option), params: vec![ty!("Int")] }))
);
parse_test_wrap_ast!("a : KoreanBBQSpecifier<Kimchi, Option<Bulgogi> >",
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<a>)",
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()
}
};
}