use metadol::ast::Expr;
use metadol::lexer::{Lexer, TokenKind};
use metadol::parser::Parser;
fn tokenize(input: &str) -> Vec<(TokenKind, String)> {
Lexer::new(input).map(|t| (t.kind, t.lexeme)).collect()
}
fn token_kinds(input: &str) -> Vec<TokenKind> {
Lexer::new(input).map(|t| t.kind).collect()
}
#[test]
fn test_idiom_open_token() {
let tokens = tokenize("[|");
assert_eq!(tokens.len(), 1);
assert_eq!(tokens[0].0, TokenKind::IdiomOpen);
assert_eq!(tokens[0].1, "[|");
}
#[test]
fn test_idiom_close_token() {
let tokens = tokenize("|]");
assert_eq!(tokens.len(), 1);
assert_eq!(tokens[0].0, TokenKind::IdiomClose);
assert_eq!(tokens[0].1, "|]");
}
#[test]
fn test_complete_idiom_bracket_tokens() {
let kinds = token_kinds("[| f a b |]");
assert_eq!(kinds.len(), 5);
assert_eq!(kinds[0], TokenKind::IdiomOpen);
assert_eq!(kinds[1], TokenKind::Identifier); assert_eq!(kinds[2], TokenKind::Identifier); assert_eq!(kinds[3], TokenKind::Identifier); assert_eq!(kinds[4], TokenKind::IdiomClose);
}
#[test]
fn test_idiom_bracket_with_whitespace() {
let kinds = token_kinds("[| f a b |]");
assert_eq!(kinds.len(), 5);
assert_eq!(kinds[0], TokenKind::IdiomOpen);
assert_eq!(kinds[4], TokenKind::IdiomClose);
}
#[test]
fn test_idiom_bracket_no_space() {
let kinds = token_kinds("[|f|]");
assert_eq!(kinds[0], TokenKind::IdiomOpen);
assert_eq!(kinds[1], TokenKind::Identifier);
assert_eq!(kinds[2], TokenKind::IdiomClose);
}
#[test]
fn test_parse_idiom_bracket_function_only() {
let input = "[| f |]";
let mut parser = Parser::new(input);
let expr = parser.parse_expr(0).unwrap();
match expr {
Expr::IdiomBracket { func, args } => {
assert!(matches!(*func, Expr::Identifier(ref name) if name == "f"));
assert_eq!(args.len(), 0);
}
_ => panic!("Expected IdiomBracket expression, got: {:?}", expr),
}
}
#[test]
fn test_parse_idiom_bracket_one_argument() {
let input = "[| f a |]";
let mut parser = Parser::new(input);
let expr = parser.parse_expr(0).unwrap();
match expr {
Expr::IdiomBracket { func, args } => {
assert!(matches!(*func, Expr::Identifier(ref name) if name == "f"));
assert_eq!(args.len(), 1);
assert!(matches!(args[0], Expr::Identifier(ref name) if name == "a"));
}
_ => panic!("Expected IdiomBracket expression, got: {:?}", expr),
}
}
#[test]
fn test_parse_idiom_bracket_two_arguments() {
let input = "[| f a b |]";
let mut parser = Parser::new(input);
let expr = parser.parse_expr(0).unwrap();
match expr {
Expr::IdiomBracket { func, args } => {
assert!(matches!(*func, Expr::Identifier(ref name) if name == "f"));
assert_eq!(args.len(), 2);
assert!(matches!(args[0], Expr::Identifier(ref name) if name == "a"));
assert!(matches!(args[1], Expr::Identifier(ref name) if name == "b"));
}
_ => panic!("Expected IdiomBracket expression, got: {:?}", expr),
}
}
#[test]
fn test_parse_idiom_bracket_three_arguments() {
let input = "[| f a b c |]";
let mut parser = Parser::new(input);
let expr = parser.parse_expr(0).unwrap();
match expr {
Expr::IdiomBracket { func, args } => {
assert!(matches!(*func, Expr::Identifier(ref name) if name == "f"));
assert_eq!(args.len(), 3);
assert!(matches!(args[0], Expr::Identifier(ref name) if name == "a"));
assert!(matches!(args[1], Expr::Identifier(ref name) if name == "b"));
assert!(matches!(args[2], Expr::Identifier(ref name) if name == "c"));
}
_ => panic!("Expected IdiomBracket expression, got: {:?}", expr),
}
}
#[test]
fn test_parse_nested_idiom_brackets() {
let input = "[| [| f a |] b |]";
let mut parser = Parser::new(input);
let expr = parser.parse_expr(0).unwrap();
match expr {
Expr::IdiomBracket { func, args } => {
match *func {
Expr::IdiomBracket {
func: inner_func,
args: inner_args,
} => {
assert!(matches!(*inner_func, Expr::Identifier(ref name) if name == "f"));
assert_eq!(inner_args.len(), 1);
assert!(matches!(inner_args[0], Expr::Identifier(ref name) if name == "a"));
}
_ => panic!("Expected nested IdiomBracket in function position"),
}
assert_eq!(args.len(), 1);
assert!(matches!(args[0], Expr::Identifier(ref name) if name == "b"));
}
_ => panic!("Expected IdiomBracket expression, got: {:?}", expr),
}
}
#[test]
fn test_parse_idiom_bracket_with_qualified_identifier() {
let input = "[| map.async transform.data list.items |]";
let mut parser = Parser::new(input);
let expr = parser.parse_expr(0).unwrap();
match expr {
Expr::IdiomBracket { func, args } => {
assert!(matches!(*func, Expr::Identifier(ref name) if name == "map.async"));
assert_eq!(args.len(), 2);
assert!(matches!(args[0], Expr::Identifier(ref name) if name == "transform.data"));
assert!(matches!(args[1], Expr::Identifier(ref name) if name == "list.items"));
}
_ => panic!("Expected IdiomBracket expression, got: {:?}", expr),
}
}
#[test]
fn test_parse_idiom_bracket_with_expressions() {
let input = "[| add (x) (y) |]";
let mut parser = Parser::new(input);
let expr = parser.parse_expr(0).unwrap();
match expr {
Expr::IdiomBracket { func, args } => {
match *func {
Expr::Call { .. } => {
assert_eq!(args.len(), 0);
}
Expr::Identifier(ref name) if name == "add" => {
assert_eq!(args.len(), 2);
}
_ => panic!(
"Expected either identifier or call as function, got: {:?}",
func
),
}
}
_ => panic!("Expected IdiomBracket expression, got: {:?}", expr),
}
}
#[test]
fn test_idiom_bracket_in_pipeline() {
let input = "data |> [| f a |] |> result";
let mut parser = Parser::new(input);
let expr = parser.parse_expr(0).unwrap();
match expr {
Expr::Binary {
op: metadol::ast::BinaryOp::Pipe,
..
} => {
}
_ => panic!("Expected pipe expression at top level"),
}
}
#[test]
fn test_idiom_bracket_with_many_arguments() {
let input = "[| func a b c d e f |]";
let mut parser = Parser::new(input);
let expr = parser.parse_expr(0).unwrap();
match expr {
Expr::IdiomBracket { func, args } => {
assert!(matches!(*func, Expr::Identifier(ref name) if name == "func"));
assert_eq!(args.len(), 6);
}
_ => panic!("Expected IdiomBracket expression"),
}
}
#[test]
fn test_idiom_bracket_with_complex_function() {
let input = "[| (f >> g) a b |]";
let mut parser = Parser::new(input);
let expr = parser.parse_expr(0).unwrap();
match expr {
Expr::IdiomBracket { func, args } => {
match *func {
Expr::Binary {
op: metadol::ast::BinaryOp::Compose,
..
} => {}
_ => panic!("Expected composition in function position"),
}
assert_eq!(args.len(), 2);
}
_ => panic!("Expected IdiomBracket expression"),
}
}
#[test]
fn test_unclosed_idiom_bracket() {
let input = "[| f a";
let mut parser = Parser::new(input);
let result = parser.parse_expr(0);
assert!(
result.is_err(),
"Should fail on unclosed idiom bracket, but got: {:?}",
result
);
}
#[test]
fn test_empty_idiom_bracket() {
let input = "[| |]";
let mut parser = Parser::new(input);
let result = parser.parse_expr(0);
assert!(
result.is_err(),
"Should fail on empty idiom bracket, but got: {:?}",
result
);
}
#[test]
fn test_mismatched_brackets() {
let input = "[| f a ]";
let mut parser = Parser::new(input);
let result = parser.parse_expr(0);
assert!(
result.is_err(),
"Should fail on mismatched brackets, but got: {:?}",
result
);
}
#[test]
fn test_only_opening_bracket() {
let input = "[|";
let mut parser = Parser::new(input);
let result = parser.parse_expr(0);
assert!(
result.is_err(),
"Should fail with only opening bracket, but got: {:?}",
result
);
}
#[test]
fn test_idiom_bracket_multiple_in_expression() {
let input = "[| f a |] |> [| g b |]";
let mut parser = Parser::new(input);
let expr = parser.parse_expr(0).unwrap();
match expr {
Expr::Binary {
op: metadol::ast::BinaryOp::Pipe,
left,
right,
} => {
assert!(matches!(*left, Expr::IdiomBracket { .. }));
assert!(matches!(*right, Expr::IdiomBracket { .. }));
}
_ => panic!("Expected pipe with idiom brackets"),
}
}
#[test]
fn test_idiom_bracket_as_function_argument() {
let input = "map([| f a |], list)";
let mut parser = Parser::new(input);
let expr = parser.parse_expr(0).unwrap();
match expr {
Expr::Call { args, .. } => {
assert_eq!(args.len(), 2);
assert!(matches!(args[0], Expr::IdiomBracket { .. }));
}
_ => panic!("Expected function call with idiom bracket"),
}
}
#[test]
fn test_idiom_bracket_in_let_binding() {
let input = "let result = [| add x y |];";
let mut parser = Parser::new(input);
let stmt = parser.parse_stmt().unwrap();
match stmt {
metadol::ast::Stmt::Let { value, .. } => {
assert!(matches!(value, Expr::IdiomBracket { .. }));
}
_ => panic!("Expected let statement with idiom bracket"),
}
}
#[test]
fn test_idiom_bracket_in_if_condition() {
let input = "if [| pred a |] { true } else { false }";
let mut parser = Parser::new(input);
let expr = parser.parse_expr(0).unwrap();
match expr {
Expr::If { condition, .. } => {
assert!(matches!(*condition, Expr::IdiomBracket { .. }));
}
_ => panic!("Expected if expression with idiom bracket condition"),
}
}
#[test]
fn test_idiom_bracket_deeply_nested() {
let input = "[| [| [| f a |] b |] c |]";
let mut parser = Parser::new(input);
let expr = parser.parse_expr(0).unwrap();
match expr {
Expr::IdiomBracket { func, .. } => match *func {
Expr::IdiomBracket {
func: inner_func, ..
} => {
assert!(matches!(*inner_func, Expr::IdiomBracket { .. }));
}
_ => panic!("Expected nested idiom bracket"),
},
_ => panic!("Expected IdiomBracket expression"),
}
}
#[test]
fn test_idiom_bracket_with_newlines() {
let input = r#"[|
f
a
b
|]"#;
let mut parser = Parser::new(input);
let expr = parser.parse_expr(0).unwrap();
match expr {
Expr::IdiomBracket { func, args } => {
assert!(matches!(*func, Expr::Identifier(ref name) if name == "f"));
assert_eq!(args.len(), 2);
}
_ => panic!("Expected IdiomBracket expression"),
}
}
#[test]
fn test_idiom_bracket_minimal_spacing() {
let input = "[|f a b|]";
let mut parser = Parser::new(input);
let expr = parser.parse_expr(0).unwrap();
match expr {
Expr::IdiomBracket { func: _, args } => {
assert_eq!(args.len(), 2);
}
_ => panic!("Expected IdiomBracket expression"),
}
}
#[test]
fn test_idiom_bracket_applicative_semantics_documentation() {
let input = "[| add x y |]";
let mut parser = Parser::new(input);
let expr = parser.parse_expr(0).unwrap();
match expr {
Expr::IdiomBracket { func, args } => {
assert!(matches!(*func, Expr::Identifier(ref name) if name == "add"));
assert_eq!(args.len(), 2);
}
_ => panic!("Expected IdiomBracket for applicative desugaring"),
}
}