use metadol::ast::*;
use metadol::parser::Parser;
mod expr {
use super::*;
#[test]
fn literal_int() {
let ast = Parser::new("42").parse_expr(0).unwrap();
assert!(matches!(ast, Expr::Identifier(_) | Expr::Literal(_)));
}
#[test]
fn literal_float() {
let ast = Parser::new("3.14").parse_expr(0).unwrap();
assert!(matches!(ast, Expr::Identifier(_) | Expr::Literal(_)));
}
#[test]
fn literal_string() {
let ast = Parser::new(r#""hello""#).parse_expr(0).unwrap();
assert!(matches!(ast, Expr::Literal(_)));
}
#[test]
fn literal_bool_true() {
let ast = Parser::new("true").parse_expr(0).unwrap();
assert!(matches!(ast, Expr::Literal(_)));
}
#[test]
fn literal_bool_false() {
let ast = Parser::new("false").parse_expr(0).unwrap();
assert!(matches!(ast, Expr::Literal(_)));
}
#[test]
fn binary_add() {
let ast = Parser::new("1 + 2").parse_expr(0).unwrap();
assert!(matches!(ast, Expr::Binary { .. }));
}
#[test]
fn binary_sub() {
let ast = Parser::new("1 - 2").parse_expr(0).unwrap();
assert!(matches!(ast, Expr::Binary { .. }));
}
#[test]
fn binary_mul() {
let ast = Parser::new("1 * 2").parse_expr(0).unwrap();
assert!(matches!(ast, Expr::Binary { .. }));
}
#[test]
fn binary_div() {
let ast = Parser::new("1 / 2").parse_expr(0).unwrap();
assert!(matches!(ast, Expr::Binary { .. }));
}
#[test]
fn binary_nested() {
let ast = Parser::new("1 + 2 * 3").parse_expr(0).unwrap();
assert!(matches!(ast, Expr::Binary { .. }));
}
#[test]
fn binary_comparison() {
let ast = Parser::new("a < b").parse_expr(0).unwrap();
assert!(matches!(ast, Expr::Binary { .. }));
}
#[test]
fn binary_logical_and() {
let ast = Parser::new("a && b").parse_expr(0).unwrap();
assert!(matches!(ast, Expr::Binary { .. }));
}
#[test]
fn binary_logical_or() {
let ast = Parser::new("a || b").parse_expr(0).unwrap();
assert!(matches!(ast, Expr::Binary { .. }));
}
#[test]
fn pipe_forward() {
let ast = Parser::new("x |> f").parse_expr(0).unwrap();
assert!(matches!(
ast,
Expr::Binary {
op: BinaryOp::Pipe,
..
}
));
}
#[test]
fn pipe_compose() {
let ast = Parser::new("f >> g").parse_expr(0).unwrap();
assert!(matches!(
ast,
Expr::Binary {
op: BinaryOp::Compose,
..
}
));
}
#[test]
fn pipe_chain() {
let ast = Parser::new("x |> f |> g |> h").parse_expr(0).unwrap();
assert!(matches!(
ast,
Expr::Binary {
op: BinaryOp::Pipe,
..
}
));
}
#[test]
fn unary_not() {
let ast = Parser::new("!x").parse_expr(0).unwrap();
assert!(matches!(ast, Expr::Unary { .. }));
}
#[test]
fn unary_neg() {
let ast = Parser::new("-x").parse_expr(0).unwrap();
assert!(matches!(ast, Expr::Unary { .. }));
}
#[test]
fn call_no_args() {
let ast = Parser::new("foo()").parse_expr(0).unwrap();
assert!(matches!(ast, Expr::Call { .. }));
}
#[test]
fn call_one_arg() {
let ast = Parser::new("foo(1)").parse_expr(0).unwrap();
assert!(matches!(ast, Expr::Call { .. }));
}
#[test]
fn call_multiple_args() {
let ast = Parser::new("foo(1, 2, 3)").parse_expr(0).unwrap();
assert!(matches!(ast, Expr::Call { .. }));
}
#[test]
fn call_nested() {
let ast = Parser::new("foo(bar(x))").parse_expr(0).unwrap();
assert!(matches!(ast, Expr::Call { .. }));
}
#[test]
fn field_access_simple() {
let ast = Parser::new("obj.field").parse_expr(0).unwrap();
assert!(matches!(ast, Expr::Member { .. } | Expr::Identifier(_)));
}
#[test]
fn field_access_chain() {
let ast = Parser::new("a.b.c.d").parse_expr(0).unwrap();
assert!(matches!(ast, Expr::Member { .. } | Expr::Identifier(_)));
}
#[test]
fn method_call() {
let ast = Parser::new("obj.method()").parse_expr(0).unwrap();
assert!(matches!(ast, Expr::Call { .. }));
}
#[test]
fn index_simple() {
let result = Parser::new("arr[0]").parse_expr(0);
assert!(result.is_ok() || result.is_err());
}
#[test]
fn index_nested() {
let result = Parser::new("matrix[i][j]").parse_expr(0);
assert!(result.is_ok() || result.is_err());
}
#[test]
fn lambda_no_params() {
let _result = Parser::new("|| { 42 }").parse_expr(0);
}
#[test]
fn lambda_one_param() {
let _result = Parser::new("|x| { x * 2 }").parse_expr(0);
}
#[test]
fn lambda_typed() {
let _result = Parser::new("|x: Int64| -> Int64 { x * 2 }").parse_expr(0);
}
#[test]
fn if_simple() {
let _result = Parser::new("if true { 1 }").parse_expr(0);
}
#[test]
fn if_else() {
let _result = Parser::new("if true { 1 } else { 2 }").parse_expr(0);
}
#[test]
fn if_else_if() {
let _result = Parser::new("if a { 1 } else if b { 2 } else { 3 }").parse_expr(0);
}
#[test]
fn match_simple() {
let input = r#"match x {
0 { "zero" }
_ { "other" }
}"#;
let _result = Parser::new(input).parse_expr(0);
}
#[test]
fn match_with_guard() {
let input = r#"match n {
x where x > 0 { "positive" }
_ { "non-positive" }
}"#;
let _result = Parser::new(input).parse_expr(0);
}
#[test]
fn block_empty() {
let _result = Parser::new("{ }").parse_expr(0);
}
#[test]
fn block_with_stmts() {
let _result = Parser::new("{ let x = 1; x + 1 }").parse_expr(0);
}
#[test]
fn parens_simple() {
let ast = Parser::new("(1 + 2)").parse_expr(0).unwrap();
let _ = ast; }
#[test]
fn parens_nested() {
let ast = Parser::new("((1 + 2) * 3)").parse_expr(0).unwrap();
let _ = ast; }
}
mod stmt {
use super::*;
#[test]
fn let_simple() {
let _result = Parser::new("let x = 1").parse_stmt();
}
#[test]
fn let_typed() {
let _result = Parser::new("let x: Int64 = 1").parse_stmt();
}
#[test]
fn return_value() {
let _result = Parser::new("return 42").parse_stmt();
}
#[test]
fn return_void() {
let _result = Parser::new("return").parse_stmt();
}
#[test]
fn for_loop() {
let _result = Parser::new("for x in items { process(x) }").parse_stmt();
}
#[test]
fn while_loop() {
let _result = Parser::new("while x > 0 { x = x - 1 }").parse_stmt();
}
#[test]
fn loop_infinite() {
let _result = Parser::new("loop { break }").parse_stmt();
}
#[test]
fn break_stmt() {
let _result = Parser::new("break").parse_stmt();
}
#[test]
fn continue_stmt() {
let _result = Parser::new("continue").parse_stmt();
}
}
mod decl {
use super::*;
#[test]
fn gene_empty() {
let _result = Parser::new("gene Empty { }").parse_file();
}
#[test]
fn gene_with_type() {
let _result = Parser::new("gene Counter { type: Int64 }").parse_file();
}
#[test]
fn gene_with_fields() {
let input = r#"gene Container {
has id: UInt64
has name: String
}"#;
let _result = Parser::new(input).parse_file();
}
#[test]
fn gene_with_constraint() {
let input = r#"gene Positive {
type: Int64
constraint positive { this.value > 0 }
}"#;
let _result = Parser::new(input).parse_file();
}
#[test]
fn gene_with_exegesis() {
let input = r#"gene Documented {
type: Int64
exegesis { This is documentation. }
}"#;
let _result = Parser::new(input).parse_file();
}
#[test]
fn trait_empty() {
let _result = Parser::new("trait Empty { }").parse_file();
}
#[test]
fn trait_with_method() {
let input = r#"trait Runnable {
is run() -> Void
}"#;
let _result = Parser::new(input).parse_file();
}
#[test]
fn trait_with_requires() {
let input = r#"trait Schedulable {
requires priority: Function<Self, Int32>
}"#;
let _result = Parser::new(input).parse_file();
}
#[test]
fn function_no_params() {
let _result = Parser::new("fun noop() { }").parse_file();
}
#[test]
fn function_with_params() {
let _result =
Parser::new("fun add(a: Int64, b: Int64) -> Int64 { return a + b }").parse_file();
}
#[test]
fn function_pub() {
let _result = Parser::new("pub fun public_fn() { }").parse_file();
}
#[test]
fn system_empty() {
let _result = Parser::new("system Empty { }").parse_file();
}
#[test]
fn module_simple() {
let _result = Parser::new("module my.module @ 1.0.0").parse_file();
}
#[test]
fn use_all() {
let _result = Parser::new("use dol.ast.*").parse_file();
}
#[test]
fn use_named() {
let _result = Parser::new("use dol.ast.{Expr, Stmt}").parse_file();
}
}
mod integration {
use super::*;
use std::fs;
#[test]
fn parse_dol_ast() {
if let Ok(content) = fs::read_to_string("dol/ast.dol") {
let result = Parser::new(&content).parse_file();
assert!(result.is_ok(), "Failed to parse dol/ast.dol");
}
}
#[test]
fn parse_dol_parser() {
if let Ok(content) = fs::read_to_string("dol/parser.dol") {
let result = Parser::new(&content).parse_file();
assert!(result.is_ok(), "Failed to parse dol/parser.dol");
}
}
#[test]
fn parse_dol_codegen() {
if let Ok(content) = fs::read_to_string("dol/codegen.dol") {
let result = Parser::new(&content).parse_file();
assert!(result.is_ok(), "Failed to parse dol/codegen.dol");
}
}
#[test]
fn parse_all_dol_files() {
let dol_dir = "dol";
if let Ok(entries) = fs::read_dir(dol_dir) {
for entry in entries.flatten() {
let path = entry.path();
if path.extension().map(|e| e == "dol").unwrap_or(false) {
let content = fs::read_to_string(&path).unwrap();
let result = Parser::new(&content).parse_file();
assert!(
result.is_ok(),
"Failed to parse {:?}: {:?}",
path,
result.err()
);
}
}
}
}
}
mod expr_extended {
use super::*;
#[test]
fn binary_modulo() {
let ast = Parser::new("a % b").parse_expr(0).unwrap();
assert!(matches!(ast, Expr::Binary { .. }));
}
#[test]
fn comparison_eq() {
let ast = Parser::new("a == b").parse_expr(0).unwrap();
assert!(matches!(ast, Expr::Binary { .. }));
}
#[test]
fn comparison_ne() {
let ast = Parser::new("a != b").parse_expr(0).unwrap();
assert!(matches!(ast, Expr::Binary { .. }));
}
#[test]
fn comparison_le() {
let ast = Parser::new("a <= b").parse_expr(0).unwrap();
assert!(matches!(ast, Expr::Binary { .. }));
}
#[test]
fn comparison_ge() {
let ast = Parser::new("a >= b").parse_expr(0).unwrap();
assert!(matches!(ast, Expr::Binary { .. }));
}
#[test]
fn unary_combined_with_binary() {
let ast = Parser::new("-a + b").parse_expr(0).unwrap();
assert!(matches!(ast, Expr::Binary { .. }));
}
#[test]
fn not_combined_with_and() {
let ast = Parser::new("!a && b").parse_expr(0).unwrap();
assert!(matches!(ast, Expr::Binary { .. }));
}
#[test]
fn call_with_trailing_comma() {
let _result = Parser::new("foo(1, 2,)").parse_expr(0);
}
#[test]
fn nested_binary_ops() {
let ast = Parser::new("a + b * c - d / e").parse_expr(0).unwrap();
assert!(matches!(ast, Expr::Binary { .. }));
}
#[test]
fn triple_or() {
let ast = Parser::new("a || b || c").parse_expr(0).unwrap();
assert!(matches!(ast, Expr::Binary { .. }));
}
#[test]
fn triple_and() {
let ast = Parser::new("a && b && c").parse_expr(0).unwrap();
assert!(matches!(ast, Expr::Binary { .. }));
}
#[test]
fn mixed_logical() {
let ast = Parser::new("a && b || c && d").parse_expr(0).unwrap();
assert!(matches!(ast, Expr::Binary { .. }));
}
#[test]
fn tuple_empty() {
let ast = Parser::new("()").parse_expr(0).unwrap();
assert!(matches!(ast, Expr::Tuple(_)));
}
#[test]
fn tuple_single() {
let _result = Parser::new("(a,)").parse_expr(0);
}
#[test]
fn tuple_multiple() {
let _result = Parser::new("(a, b, c)").parse_expr(0);
}
#[test]
fn list_empty() {
let ast = Parser::new("[]").parse_expr(0).unwrap();
assert!(matches!(ast, Expr::List(_)));
}
#[test]
fn list_single() {
let ast = Parser::new("[a]").parse_expr(0).unwrap();
assert!(matches!(ast, Expr::List(_)));
}
#[test]
fn list_multiple() {
let ast = Parser::new("[a, b, c]").parse_expr(0).unwrap();
assert!(matches!(ast, Expr::List(_)));
}
#[test]
fn quote_expr() {
let _result = Parser::new("'expr").parse_expr(0);
}
#[test]
fn idiom_bracket_expr() {
let _result = Parser::new("[| a + b |]").parse_expr(0);
}
}
mod stmt_extended {
use super::*;
#[test]
fn assignment() {
let _result = Parser::new("x = 42").parse_stmt();
}
#[test]
fn compound_assignment() {
let _result = Parser::new("x += 1").parse_stmt();
}
#[test]
fn for_with_range() {
let _result = Parser::new("for i in 0..10 { print(i) }").parse_stmt();
}
#[test]
fn while_with_complex_condition() {
let _result = Parser::new("while x > 0 && y < 10 { process() }").parse_stmt();
}
#[test]
fn nested_loop() {
let input = r#"for i in items {
for j in other {
process(i, j)
}
}"#;
let _result = Parser::new(input).parse_stmt();
}
#[test]
fn if_in_loop() {
let input = r#"for x in items {
if x > 0 {
process(x)
}
}"#;
let _result = Parser::new(input).parse_stmt();
}
#[test]
fn break_with_value() {
let _result = Parser::new("break 42").parse_stmt();
}
#[test]
fn return_complex_expr() {
let _result = Parser::new("return a + b * c").parse_stmt();
}
}
mod decl_extended {
use super::*;
#[test]
fn gene_with_multiple_fields() {
let input = r#"gene MultiField {
has a: Int64
has b: String
has c: Bool
has d: Float64
}"#;
let _result = Parser::new(input).parse_file();
}
#[test]
fn trait_multiple_methods() {
let input = r#"trait MultiMethod {
is method_a() -> Void
is method_b(x: Int64) -> Int64
is method_c(a: Int64, b: String) -> Bool
}"#;
let _result = Parser::new(input).parse_file();
}
#[test]
fn function_with_type_params() {
let input = "pub fun generic<T>(x: T) -> T { return x }";
let _result = Parser::new(input).parse_file();
}
#[test]
fn gene_with_default_values() {
let input = r#"gene WithDefaults {
has count: Int64 = 0
has name: String = "default"
}"#;
let _result = Parser::new(input).parse_file();
}
#[test]
fn system_with_components() {
let input = r#"system MySystem {
has processor: Processor
has scheduler: Scheduler
}"#;
let _result = Parser::new(input).parse_file();
}
#[test]
fn constraint_with_complex_body() {
let input = r#"gene Positive {
type: Int64
constraint positive {
this.value > 0 && this.value < 100
}
}"#;
let _result = Parser::new(input).parse_file();
}
#[test]
fn use_with_multiple_items() {
let _result = Parser::new("use dol.ast.{Expr, Stmt, Decl, Pattern}").parse_file();
}
#[test]
fn module_with_full_path() {
let _result = Parser::new("module com.example.mypackage @ 1.0.0").parse_file();
}
#[test]
fn evolution_declaration() {
let input = r#"evolves MyGene @ 1.0.0 -> 2.0.0 {
adds new_field: String because "new feature"
}"#;
let _result = Parser::new(input).parse_file();
}
#[test]
fn pub_gene() {
let _result = Parser::new("pub gene PublicGene { }").parse_file();
}
}
mod type_expr {
use super::*;
#[test]
fn simple_type() {
let result = Parser::new("fun f(x: Int64) { }").parse_file();
assert!(result.is_ok());
}
#[test]
fn generic_type() {
let result = Parser::new("fun f(x: List<Int64>) { }").parse_file();
assert!(result.is_ok());
}
#[test]
fn nested_generic() {
let result = Parser::new("fun f(x: Map<String, List<Int64>>) { }").parse_file();
assert!(result.is_ok());
}
#[test]
fn option_type() {
let result = Parser::new("fun f(x: Option<String>) { }").parse_file();
assert!(result.is_ok());
}
#[test]
fn function_type() {
let result = Parser::new("fun f(callback: Function<Int64, String>) { }").parse_file();
assert!(result.is_ok());
}
#[test]
fn unit_type() {
let _result = Parser::new("fun f() -> () { }").parse_file();
}
#[test]
fn tuple_type() {
let _result = Parser::new("fun f() -> (Int64, String) { }").parse_file();
}
}
mod patterns {
use super::*;
#[test]
fn match_wildcard() {
let input = r#"match x {
_ { default_action() }
}"#;
let _result = Parser::new(input).parse_expr(0);
}
#[test]
fn match_identifier() {
let input = r#"match x {
y { use_y(y) }
}"#;
let _result = Parser::new(input).parse_expr(0);
}
#[test]
fn match_literal_int() {
let input = r#"match x {
0 { zero() }
1 { one() }
_ { other() }
}"#;
let _result = Parser::new(input).parse_expr(0);
}
#[test]
fn match_literal_string() {
let input = r#"match name {
"alice" { greet_alice() }
"bob" { greet_bob() }
_ { greet_other() }
}"#;
let _result = Parser::new(input).parse_expr(0);
}
#[test]
fn match_constructor() {
let input = r#"match opt {
Some(x) { use_x(x) }
None { handle_none() }
}"#;
let _result = Parser::new(input).parse_expr(0);
}
#[test]
fn match_nested_pattern() {
let input = r#"match result {
Ok(Some(value)) { use_value(value) }
Ok(None) { handle_none() }
Err(e) { handle_error(e) }
}"#;
let _result = Parser::new(input).parse_expr(0);
}
}