diff --git a/agdb/src/api_def.rs b/agdb/src/api_def.rs deleted file mode 100644 index 514dd493..00000000 --- a/agdb/src/api_def.rs +++ /dev/null @@ -1,230 +0,0 @@ -pub mod enum_def; -pub mod expression_def; -pub mod function_def; -pub mod struct_def; -pub mod tuple_def; - -pub use enum_def::Enum; -pub use expression_def::Expression; -pub use expression_def::Literal; -pub use expression_def::Op; -pub use function_def::Function; -pub use struct_def::Struct; -pub use tuple_def::Tuple; - -pub trait ImplDefinition { - fn functions() -> &'static [Function] { - &[] - } -} - -pub trait TypeDefinition: ImplDefinition { - fn type_def() -> Type; - fn generic_type_names() -> Vec<&'static str> { - match Self::type_def() { - Type::Enum(e) => e.generics.iter().map(|g| g.name).collect(), - Type::Struct(s) => s.generics.iter().map(|g| g.name).collect(), - Type::Tuple(t) => t.generics.iter().map(|g| g.name).collect(), - } - } -} - -pub enum Type { - Enum(Enum), - Struct(Struct), - Tuple(Tuple), -} - -impl Type { - #[allow(dead_code)] - fn functions(&self) -> &'static [Function] { - match self { - Type::Enum(e) => e.functions, - Type::Struct(s) => s.functions, - Type::Tuple(t) => t.functions, - } - } - fn name(&self) -> &'static str { - match self { - Type::Enum(e) => e.name, - Type::Struct(s) => s.name, - Type::Tuple(t) => t.name, - } - } -} - -pub struct NamedType { - pub name: &'static str, - pub ty: Option Type>, -} - -pub struct Generic { - pub name: &'static str, - pub bounds: &'static [&'static str], -} - -// --- Rust types implementations --- // - -#[macro_export] -macro_rules! impl_type { - ($ty:ty) => { - impl $crate::api_def::ImplDefinition for $ty {} - - impl $crate::api_def::TypeDefinition for $ty { - fn type_def() -> $crate::api_def::Type { - $crate::api_def::Type::Struct($crate::api_def::struct_def::Struct { - name: stringify!($ty), - generics: &[], - fields: &[], - functions: &[], - }) - } - } - }; -} - -impl_type!(bool); -impl_type!(i8); -impl_type!(i16); -impl_type!(i32); -impl_type!(i64); -impl_type!(u8); -impl_type!(u16); -impl_type!(u32); -impl_type!(u64); -impl_type!(f64); -impl_type!(String); -impl_type!(&str); -impl_type!(()); - -impl TypeDefinition for Option { - fn type_def() -> Type { - Type::Struct(Struct { - name: "Option", - generics: &[Generic { - name: "T", - bounds: &[], - }], - fields: &[], - functions: &[], - }) - } - - fn generic_type_names() -> Vec<&'static str> { - vec![T::type_def().name()] - } -} - -impl ImplDefinition for Option { - fn functions() -> &'static [Function] { - &[] - } -} - -impl TypeDefinition for Vec { - fn type_def() -> Type { - Type::Struct(Struct { - name: "Vec", - generics: &[Generic { - name: "T", - bounds: &[], - }], - fields: &[], - functions: &[], - }) - } - - fn generic_type_names() -> Vec<&'static str> { - vec![T::type_def().name()] - } -} - -impl ImplDefinition for Vec { - fn functions() -> &'static [Function] { - &[] - } -} - -impl TypeDefinition for Result { - fn type_def() -> Type { - Type::Struct(Struct { - name: "Result", - generics: &[ - Generic { - name: "T", - bounds: &[], - }, - Generic { - name: "E", - bounds: &[], - }, - ], - fields: &[], - functions: &[], - }) - } - - fn generic_type_names() -> Vec<&'static str> { - vec![T::type_def().name(), E::type_def().name()] - } -} - -impl ImplDefinition for Result { - fn functions() -> &'static [Function] { - &[] - } -} - -impl TypeDefinition for (T1, T2) { - fn type_def() -> Type { - Type::Tuple(Tuple { - name: "Tuple2", - generics: &[ - Generic { - name: "T1", - bounds: &[], - }, - Generic { - name: "T2", - bounds: &[], - }, - ], - fields: &[], - functions: &[], - }) - } - - fn generic_type_names() -> Vec<&'static str> { - vec![T1::type_def().name(), T2::type_def().name()] - } -} - -impl ImplDefinition for (T1, T2) { - fn functions() -> &'static [Function] { - &[] - } -} - -impl TypeDefinition for &[T] { - fn type_def() -> Type { - Type::Struct(Struct { - name: "Slice", - generics: &[Generic { - name: "T", - bounds: &[], - }], - fields: &[], - functions: &[], - }) - } - - fn generic_type_names() -> Vec<&'static str> { - vec![T::type_def().name()] - } -} - -impl ImplDefinition for &[T] { - fn functions() -> &'static [Function] { - &[] - } -} diff --git a/agdb/src/api_def/enum_def.rs b/agdb/src/api_def/enum_def.rs deleted file mode 100644 index d03d562c..00000000 --- a/agdb/src/api_def/enum_def.rs +++ /dev/null @@ -1,129 +0,0 @@ -use crate::api_def::Function; -use crate::api_def::Generic; -use crate::api_def::NamedType; - -pub struct Enum { - pub name: &'static str, - pub generics: &'static [Generic], - pub variants: &'static [NamedType], - pub functions: &'static [Function], -} - -#[cfg(test)] -mod tests { - use crate::api_def::Type; - use crate::api_def::TypeDefinition; - - #[test] - fn enum_definition() { - #[derive(agdb::TypeDefImpl)] - enum SomeEnum {} - - let enum_def = SomeEnum::type_def(); - - if let Type::Enum(e) = enum_def { - assert_eq!(e.name, "SomeEnum"); - assert_eq!(e.generics.len(), 0); - assert_eq!(e.variants.len(), 0); - } else { - panic!("Expected Type::Enum"); - } - } - - #[test] - fn enum_definition_with_variants() { - #[derive(agdb::TypeDefImpl)] - #[allow(dead_code)] - enum SimpleEnum { - A, - B, - C, - } - - let enum_def = SimpleEnum::type_def(); - - if let Type::Enum(e) = enum_def { - assert_eq!(e.variants.len(), 3); - assert_eq!(e.variants[0].name, "A"); - assert_eq!(e.variants[1].name, "B"); - assert_eq!(e.variants[2].name, "C"); - } else { - panic!("Expected Type::Enum"); - } - } - - #[test] - fn enum_definition_with_generics() { - #[derive(agdb::TypeDefImpl)] - #[allow(dead_code)] - enum GenericEnum { - Variant1 { field1: T }, - Variant2 { field2: U }, - } - - let enum_def = GenericEnum::::type_def(); - - if let Type::Enum(e) = enum_def { - assert_eq!(e.generics.len(), 2); - assert_eq!(e.generics[0].name, "T"); - assert_eq!(e.generics[1].name, "U"); - assert_eq!(e.variants.len(), 2); - assert_eq!(e.variants[0].name, "Variant1"); - if let Type::Struct(s) = (e.variants[0].ty.unwrap())() { - assert_eq!(s.fields.len(), 1); - assert_eq!(s.fields[0].name, "field1"); - } else { - panic!("Expected Variant1 field type to be Struct"); - } - assert_eq!(e.variants[1].name, "Variant2"); - if let Type::Struct(s) = (e.variants[1].ty.unwrap())() { - assert_eq!(s.fields.len(), 1); - assert_eq!(s.fields[0].name, "field2"); - } else { - panic!("Expected Variant2 field type to be Struct"); - } - } else { - panic!("Expected Type::Enum"); - } - } - - #[test] - fn enum_definition_variant_of_another_type() { - #[derive(agdb::TypeDef)] - struct OtherType; - - #[agdb::impl_def()] - #[allow(dead_code)] - impl OtherType { - fn foo() {} - } - - #[derive(agdb::TypeDefImpl)] - #[allow(dead_code)] - enum OtherTypeEnum { - TupleVariant(OtherType), - } - - let enum_def = OtherTypeEnum::type_def(); - - if let Type::Enum(e) = enum_def { - assert_eq!(e.variants.len(), 1); - assert_eq!(e.variants[0].name, "TupleVariant"); - if let Type::Tuple(t) = (e.variants[0].ty.unwrap())() { - if let Type::Struct(s) = (t.fields[0])() { - assert_eq!(s.name, "OtherType"); - s.functions - .iter() - .find(|f| f.name == "foo") - .expect("Expected function foo"); - } else { - panic!("Expected TupleVariant field type to be OtherType Struct"); - } - } else { - panic!("Expected UnitVariant type to be None"); - } - } else { - panic!("Expected Type::Enum"); - } - } -} diff --git a/agdb/src/api_def/expression_def.rs b/agdb/src/api_def/expression_def.rs deleted file mode 100644 index aa00118c..00000000 --- a/agdb/src/api_def/expression_def.rs +++ /dev/null @@ -1,969 +0,0 @@ -use crate::api_def::Type; -use crate::api_def::function_def::Function; -use std::fmt::Display; -use std::fmt::Formatter; -use std::fmt::Result as FmtResult; - -pub enum Expression { - // [1, 2, 3] - Array(&'static [Expression]), - - // target = value - Assign { - target: &'static Expression, - value: &'static Expression, - }, - - // expr.await - Await(&'static Expression), - - // 1 < 2 - Binary { - op: Op, - left: &'static Expression, - right: &'static Expression, - }, - - // { ... } - Block(&'static [Expression]), - - // break; - Break, - - // func(args) - // obj.method(args) - Call { - recipient: Option<&'static Expression>, - function: &'static Expression, - args: &'static [Expression], - }, - - // |args| -> ret { ... } - // |args| { ... } - Closure(Function), - - // continue; - Continue, - - // obj.field - FieldAccess { - base: &'static Expression, - field: &'static str, - }, - - // for pattern in iterable { body } - For { - pattern: &'static Expression, - iterable: &'static Expression, - body: &'static Expression, - }, - - // format!("{}", args) - Format { - format_string: &'static str, - args: &'static [Expression], - }, - - // varname - Ident(&'static str), - - // if condition { then_branch } else { else_branch } - // if condition { then_branch } - If { - condition: &'static Expression, - then_branch: &'static Expression, - else_branch: Option<&'static Expression>, - }, - - // array[index] - Index { - base: &'static Expression, - index: &'static Expression, - }, - - // let name: Type = value; - // let name = value; - // let name; - Let { - name: &'static Expression, - ty: Option Type>, - value: Option<&'static Expression>, - }, - - // "literal" - // 42 - // true - Literal(Literal), - - // Some::Type::foo() - Path { - ident: &'static str, - parent: Option<&'static Expression>, - generics: &'static [fn() -> Type], - }, - - // &expr - Reference(&'static Expression), - - // return expr; - Return(Option<&'static Expression>), - - // Struct { field1: value1, field2: value2 } - Struct { - name: &'static Expression, - fields: &'static [(&'static str, Expression)], - }, - - // Struct { field1, field2 } - StructPattern { - name: &'static Expression, - fields: &'static [Expression], - }, - - // call()? - Try(&'static Expression), - - // (x, y) - Tuple(&'static [Expression]), - - // Tuple(expr, expr2, ...) - TupleStruct { - name: &'static Expression, - expressions: &'static [Expression], - }, - - // base.index - TupleAccess { - base: &'static Expression, - index: u32, - }, - - // -expr - // !expr - Unary { - op: Op, - expr: &'static Expression, - }, - - // while condition { body } - While { - condition: &'static Expression, - body: &'static Expression, - }, - - // _ - Wild, -} - -pub enum Literal { - I64(i64), - F64(f64), - String(&'static str), - Bool(bool), -} - -pub enum Op { - // + - Add, - // += - AddAssign, - // - - Sub, - // -= - SubAssign, - // * - Mul, - // *= - MulAssign, - // / - Div, - // /= - DivAssign, - // % - Rem, - // %= - RemAssign, - // && - And, - // || - Or, - // ^ - BitXor, - // ^= - BitXorAssign, - // & - BitAnd, - // &= - BitAndAssign, - // | - BitOr, - // |= - BitOrAssign, - // << - Shl, - // <<= - ShlAssign, - // >> - Shr, - // >>= - ShrAssign, - // == - Eq, - // < - Lt, - // <= - Le, - // != - Ne, - // >= - Ge, - // > - Gt, - // ! - Not, - // - - Neg, - // * - Deref, -} - -impl Display for Op { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - let op_str = match self { - Op::Add => "+", - Op::Sub => "-", - Op::Mul => "*", - Op::Div => "/", - Op::Rem => "%", - Op::And => "&&", - Op::Or => "||", - Op::BitXor => "^", - Op::BitAnd => "&", - Op::BitOr => "|", - Op::Shl => "<<", - Op::Shr => ">>", - Op::Eq => "==", - Op::Lt => "<", - Op::Le => "<=", - Op::Ne => "!=", - Op::Ge => ">=", - Op::Gt => ">", - Op::Not => "!", - Op::Neg => "-", - Op::AddAssign => "+=", - Op::SubAssign => "-=", - Op::MulAssign => "*=", - Op::DivAssign => "/=", - Op::RemAssign => "%=", - Op::BitXorAssign => "^=", - Op::BitAndAssign => "&=", - Op::BitOrAssign => "|=", - Op::ShlAssign => "<<=", - Op::ShrAssign => ">>=", - Op::Deref => "*", - }; - write!(f, "{}", op_str) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::api_def::TypeDefinition; - - fn write_expression(expr: &Expression) -> String { - match expr { - Expression::Array(elements) => format!( - "[{}]", - elements - .iter() - .map(write_expression) - .collect::>() - .join(", ") - ), - Expression::Assign { target, value } => { - format!("{} = {}", write_expression(target), write_expression(value)) - } - Expression::Await(expression) => format!("{}.await", write_expression(expression)), - Expression::Binary { op, left, right } => format!( - "{} {op} {}", - write_expression(left), - write_expression(right) - ), - Expression::Block(expressions) => format!( - "{{\n{}\n}}", - expressions - .iter() - .map(write_expression) - .collect::>() - .join(";\n") - ), - Expression::Break => "break".to_string(), - Expression::Call { - recipient, - function, - args, - } => format!( - "{}{}({})", - match recipient { - Some(rec) => format!("{}.", write_expression(rec)), - None => "".to_string(), - }, - write_expression(function), - args.iter() - .map(write_expression) - .collect::>() - .join(", ") - ), - Expression::Closure(function) => format!( - "|{}|{} {{\n{}\n}}", - function - .args - .iter() - .map(|arg| arg.name) - .collect::>() - .join(", "), - match function.ret { - Some(ret_fn) => format!(" -> {}", (ret_fn)().name()), - None => "".to_string(), - }, - function - .expressions - .iter() - .map(write_expression) - .collect::>() - .join(";\n") - ), - Expression::Continue => "continue".to_string(), - Expression::FieldAccess { base, field } => { - format!("{}.{field}", write_expression(base)) - } - Expression::For { - pattern, - iterable, - body, - } => format!( - "for {} in {} {}", - write_expression(pattern), - write_expression(iterable), - write_expression(body) - ), - Expression::Format { - format_string, - args, - } => { - let args_str = args - .iter() - .map(write_expression) - .collect::>() - .join(", "); - format!("format!({}, {})", format_string, args_str) - } - Expression::If { - condition, - then_branch, - else_branch, - } => format!( - "if {} {}{}", - write_expression(condition), - write_expression(then_branch), - match else_branch { - Some(else_br) => format!(" else {}", write_expression(else_br)), - None => "".to_string(), - } - ), - Expression::Index { base, index } => { - format!("{}[{}]", write_expression(base), write_expression(index)) - } - Expression::Let { name, ty, value } => format!( - "let {}{}{}", - write_expression(name), - match ty { - Some(ty_fn) => format!(": {}", (ty_fn)().name()), - None => "".to_string(), - }, - match value { - Some(val) => format!(" = {}", write_expression(val)), - None => "".to_string(), - } - ), - Expression::Literal(literal_value) => match literal_value { - Literal::I64(val) => val.to_string(), - Literal::F64(val) => val.to_string(), - Literal::String(val) => val.to_string(), - Literal::Bool(val) => val.to_string(), - }, - Expression::Path { - ident, - parent, - generics, - } => { - let parent_str = match parent { - Some(p) => format!("{}::", write_expression(p)), - None => "".to_string(), - }; - let generics_str = if generics.is_empty() { - "".to_string() - } else { - let gens = generics - .iter() - .map(|gen_fn| (gen_fn)().name()) - .collect::>() - .join(", "); - format!("<{}>", gens) - }; - format!("{}{}{}", parent_str, ident, generics_str) - } - Expression::Reference(expr) => format!("&{}", write_expression(expr)), - Expression::Return(expression) => match expression { - Some(expr) => format!("return {}", write_expression(expr)), - None => "return".to_string(), - }, - Expression::Struct { name, fields } => { - let fields_str = fields - .iter() - .map(|(field_name, expr)| format!("{}: {}", field_name, write_expression(expr))) - .collect::>() - .join(", "); - format!("{} {{ {} }}", write_expression(name), fields_str) - } - Expression::StructPattern { name, fields } => { - let fields_str = fields - .iter() - .map(write_expression) - .collect::>() - .join(", "); - format!("{} {{ {} }}", write_expression(name), fields_str) - } - Expression::Try(expression) => { - format!("{}?", write_expression(expression)) - } - Expression::Tuple(expressions) => { - format!( - "({})", - expressions - .iter() - .map(write_expression) - .collect::>() - .join(", ") - ) - } - Expression::TupleAccess { base, index } => { - format!("{}.{index}", write_expression(base)) - } - Expression::TupleStruct { name, expressions } => format!( - "{}({})", - write_expression(name), - expressions - .iter() - .map(write_expression) - .collect::>() - .join(", ") - ), - Expression::Unary { op, expr } => format!("{}{}", op, write_expression(expr)), - Expression::Ident(v) => v.to_string(), - Expression::While { condition, body } => format!( - "while {} {}", - write_expression(condition), - write_expression(body) - ), - Expression::Wild => "_".to_string(), - } - } - - #[test] - fn array() { - #[derive(agdb::TypeDef)] - struct StructWithArrayExpr; - - #[agdb::impl_def()] - #[allow(dead_code)] - impl StructWithArrayExpr { - fn get_array() { - let _ = [1, 2, 3]; - let _arr = &[1]; - let _arr_of_arrs = [[1, 2], [3, 4]]; - } - } - - let e = write_expression(&StructWithArrayExpr::type_def().functions()[0].expressions[0]); - assert_eq!(e, "let _ = [1, 2, 3]"); - - let e = write_expression(&StructWithArrayExpr::type_def().functions()[0].expressions[1]); - assert_eq!(e, "let _arr = &[1]"); - - let e = write_expression(&StructWithArrayExpr::type_def().functions()[0].expressions[2]); - assert_eq!(e, "let _arr_of_arrs = [[1, 2], [3, 4]]"); - } - - #[test] - fn assign() { - #[derive(agdb::TypeDef)] - struct StructWithAssignExpr; - - #[agdb::impl_def()] - #[allow(dead_code)] - impl StructWithAssignExpr { - fn assign_example() { - let mut _x: i32 = 5; - _x = 10; - _x += 2; - } - } - - let e = write_expression(&StructWithAssignExpr::type_def().functions()[0].expressions[0]); - assert_eq!(e, "let _x: i32 = 5"); - - let e = write_expression(&StructWithAssignExpr::type_def().functions()[0].expressions[1]); - assert_eq!(e, "_x = 10"); - - let e = write_expression(&StructWithAssignExpr::type_def().functions()[0].expressions[2]); - assert_eq!(e, "_x += 2"); - } - - #[test] - fn async_block() { - #[derive(agdb::TypeDef)] - struct StructWithAsyncBlock; - - #[agdb::impl_def()] - #[allow(dead_code)] - impl StructWithAsyncBlock { - async fn async_example() { - let _ = async { 42 }.await; - } - } - - let e = write_expression(&StructWithAsyncBlock::type_def().functions()[0].expressions[0]); - assert_eq!(e, "let _ = {\nreturn 42\n}.await"); - } - - #[test] - fn block() { - #[derive(agdb::TypeDef)] - struct StructWithBlock; - - #[agdb::impl_def()] - #[allow(dead_code)] - impl StructWithBlock { - fn block_example() { - let _ = { - let x = 10; - x + 5 - }; - } - } - - let e = write_expression(&StructWithBlock::type_def().functions()[0].expressions[0]); - assert_eq!(e, "let _ = {\nlet x = 10;\nreturn x + 5\n}"); - } - - #[test] - fn for_loop() { - #[derive(agdb::TypeDef)] - struct StructWithForLoop; - - #[agdb::impl_def()] - #[allow(dead_code)] - impl StructWithForLoop { - fn for_loop_example() { - let ar = [0, 1, 2]; - - for _i in ar {} - - for i in ar { - if i == 1 { - break; - } - } - - for _i in ar { - continue; - } - } - } - - let e = write_expression(&StructWithForLoop::type_def().functions()[0].expressions[1]); - assert_eq!(e, "for _i in ar {\n\n}"); - - let e = write_expression(&StructWithForLoop::type_def().functions()[0].expressions[2]); - assert_eq!(e, "for i in ar {\nif i == 1 {\nbreak\n}\n}"); - - let e = write_expression(&StructWithForLoop::type_def().functions()[0].expressions[3]); - assert_eq!(e, "for _i in ar {\ncontinue\n}"); - } - - #[test] - fn call() { - #[derive(agdb::TypeDef)] - struct StructWithCall; - - fn bar(_i: i32, _s: &str) {} - - #[agdb::impl_def()] - #[allow(dead_code)] - impl StructWithCall { - fn foo() { - bar(32, "hello"); - } - } - - let e = write_expression(&StructWithCall::type_def().functions()[0].expressions[0]); - assert_eq!(e, "bar(32, \"hello\")"); - } - - #[test] - fn cast() { - #[derive(agdb::TypeDef)] - struct StructWithCast; - - #[agdb::impl_def()] - #[allow(dead_code)] - impl StructWithCast { - fn cast_example() -> f64 { - let x = 42; - x as f64 - } - } - - let e = write_expression(&StructWithCast::type_def().functions()[0].expressions[1]); - assert_eq!(e, "x"); - } - - #[test] - fn closure() { - #[derive(agdb::TypeDef)] - struct StructWithClosure; - - #[agdb::impl_def()] - #[allow(dead_code)] - impl StructWithClosure { - fn closure_example() { - let _add = |a: i32, b: i32| -> i32 { a + b }; - let sub = |x| x; - let _with_body = || { - let result = 5; - if result == 5 { - return result; - } - result - }; - sub(5); - } - } - - let e = write_expression(&StructWithClosure::type_def().functions()[0].expressions[0]); - assert_eq!(e, "let _add = |a, b| -> i32 {\nreturn a + b\n}"); - let e = write_expression(&StructWithClosure::type_def().functions()[0].expressions[1]); - assert_eq!(e, "let sub = |x| {\nreturn x\n}"); - let e = write_expression(&StructWithClosure::type_def().functions()[0].expressions[2]); - assert_eq!( - e, - "let _with_body = || {\nlet result = 5;\nif result == 5 {\nreturn result\n};\nreturn result\n}" - ); - } - - #[test] - fn const_block() { - #[derive(agdb::TypeDef)] - struct StructWithConstBlock; - - #[agdb::impl_def()] - #[allow(dead_code)] - impl StructWithConstBlock { - fn const_block_example() { - let _ = const { 42 }; - } - } - - let e = write_expression(&StructWithConstBlock::type_def().functions()[0].expressions[0]); - assert_eq!(e, "let _ = {\nreturn 42\n}"); - } - - #[test] - fn field_access() { - #[derive(agdb::TypeDef)] - struct StructWithFieldAccess; - - #[derive(agdb::TypeDefImpl)] - struct SomeStructWithField { - field1: i32, - } - - #[derive(agdb::TypeDefImpl)] - struct SomeTupleWithField(i32); - - #[agdb::impl_def()] - #[allow(dead_code)] - impl StructWithFieldAccess { - fn field_access_example(s: SomeStructWithField, s2: SomeTupleWithField) { - let _ = s.field1; - let _ = s2.0; - } - } - - let e = write_expression(&StructWithFieldAccess::type_def().functions()[0].expressions[0]); - assert_eq!(e, "let _ = s.field1"); - let e = write_expression(&StructWithFieldAccess::type_def().functions()[0].expressions[1]); - assert_eq!(e, "let _ = s2.0"); - } - - #[test] - fn index() { - #[derive(agdb::TypeDef)] - struct StructWithIndex; - - #[agdb::impl_def()] - #[allow(dead_code)] - impl StructWithIndex { - fn index_example() { - let arr = [10, 20, 30]; - let _ = arr[1]; - } - } - - let e = write_expression(&StructWithIndex::type_def().functions()[0].expressions[1]); - assert_eq!(e, "let _ = arr[1]"); - } - - #[test] - fn loop_expr() { - #[derive(agdb::TypeDef)] - struct StructWithLoop; - - #[agdb::impl_def()] - #[allow(dead_code)] - impl StructWithLoop { - fn loop_example() { - let mut i = 0; - loop { - if i >= 5 { - break; - } - i += 1; - } - } - } - - let e = write_expression(&StructWithLoop::type_def().functions()[0].expressions[1]); - assert_eq!(e, "while true {\nif i >= 5 {\nbreak\n};\ni += 1\n}"); - } - - #[test] - fn macro_expr() { - #[derive(agdb::TypeDef)] - struct StructWithMacro; - - #[agdb::impl_def()] - #[allow(dead_code)] - impl StructWithMacro { - fn macro_example() { - let _ = vec![42]; - let _ = format!("Hello, {}", 42); - let a = 42; - let _ = format!("Value: {a}"); - let _ = format!("{a} {} {a}", 42); - } - } - - // Currently, macro parsing is not implemented, so this test is a placeholder. - let e = write_expression(&StructWithMacro::type_def().functions()[0].expressions[0]); - assert_eq!(e, "let _ = [42]"); - let e = write_expression(&StructWithMacro::type_def().functions()[0].expressions[1]); - assert_eq!(e, "let _ = format!(\"Hello, {}\", 42)"); - let e = write_expression(&StructWithMacro::type_def().functions()[0].expressions[3]); - assert_eq!(e, "let _ = format!(\"Value: {}\", a)"); - let e = write_expression(&StructWithMacro::type_def().functions()[0].expressions[4]); - assert_eq!(e, "let _ = format!(\"{} {} {}\", a, 42, a)"); - } - - #[test] - fn match_expr() { - #[derive(agdb::TypeDef)] - struct StructWithMatch; - - #[derive(agdb::TypeDefImpl)] - #[allow(dead_code)] - enum E { - A, - B, - C(i32), - D { x: i32 }, - } - - #[agdb::impl_def()] - #[allow(dead_code)] - impl StructWithMatch { - fn match_example(x: i32) { - match x { - 0 => (), - 1 | 2 => (), - _ => (), - } - } - fn match_enum(e: E) -> i32 { - let x = 2; - match e { - E::A => 1, - E::B if x == 2 => 2, - E::C(i) => i, - E::D { x } => x, - _ => 4, - } - } - } - - // Currently, match parsing is not implemented, so this test is a placeholder. - let e = write_expression(&StructWithMatch::type_def().functions()[0].expressions[0]); - assert_eq!( - e, - "if x == 0 {\n\n} else if x == 1 || x == 2 {\n\n} else {\n\n}" - ); - let e = write_expression(&StructWithMatch::type_def().functions()[1].expressions[1]); - assert_eq!( - e, - "if e == E::A {\n1\n} else if e == E::B && x == 2 {\n2\n} else if e == E::C(i) {\ni\n} else if e == E::D { x } {\nx\n} else {\n4\n}" - ); - } - - #[test] - fn method_call() { - #[derive(agdb::TypeDef)] - struct StructWithMethodCall; - - #[agdb::impl_def()] - #[allow(dead_code)] - impl StructWithMethodCall { - fn foo(&self, x: i32) -> i32 { - x - } - - fn standalone(x: T) -> T { - x - } - - fn bar(&self) -> i32 { - self.foo(42); - StructWithMethodCall::standalone::(42) - } - } - - let e = write_expression(&StructWithMethodCall::type_def().functions()[2].expressions[0]); - assert_eq!(e, "self.foo(42)"); - - let e = write_expression(&StructWithMethodCall::type_def().functions()[2].expressions[1]); - assert_eq!(e, "StructWithMethodCall::standalone(42)"); - } - - #[test] - fn struct_expr() { - #[derive(agdb::TypeDef)] - struct StructWithStructExpr; - - #[derive(agdb::TypeDefImpl)] - #[allow(dead_code)] - struct Point { - x: i32, - y: i32, - } - - #[agdb::impl_def()] - #[allow(dead_code)] - impl StructWithStructExpr { - fn create_point() { - let _ = Point { x: 10, y: 20 }; - } - } - - let e = write_expression(&StructWithStructExpr::type_def().functions()[0].expressions[0]); - assert_eq!(e, "let _ = Point { x: 10, y: 20 }"); - } - - #[test] - fn try_expr() { - #[derive(agdb::TypeDef)] - struct StructWithTryExpr; - - fn foo() -> Result { - Ok(42) - } - - #[agdb::impl_def()] - #[allow(dead_code)] - impl StructWithTryExpr { - fn try_example() -> Result { - let v = foo()?; - Ok(v) - } - } - - let e = write_expression(&StructWithTryExpr::type_def().functions()[0].expressions[0]); - assert_eq!(e, "let v = foo()?"); - } - - #[test] - fn tuple() { - #[derive(agdb::TypeDef)] - struct StructWithTuple; - - #[agdb::impl_def()] - #[allow(dead_code)] - impl StructWithTuple { - fn tuple_example() { - let t = (1, "hello", true); - let (_a, _b, _c) = t; - } - } - - let e = write_expression(&StructWithTuple::type_def().functions()[0].expressions[0]); - assert_eq!(e, "let t = (1, \"hello\", true)"); - - let e = write_expression(&StructWithTuple::type_def().functions()[0].expressions[1]); - assert_eq!(e, "let (_a, _b, _c) = t"); - } - - #[test] - fn unary() { - #[derive(agdb::TypeDef)] - struct StructWithUnary; - - #[agdb::impl_def()] - #[allow(dead_code)] - impl StructWithUnary { - fn unary_example(v: bool) { - let _ = -42; - let _ = !v; - let x = 10; - let y = &x; - let _ = *y; - } - } - - let e = write_expression(&StructWithUnary::type_def().functions()[0].expressions[0]); - assert_eq!(e, "let _ = -42"); - let e = write_expression(&StructWithUnary::type_def().functions()[0].expressions[1]); - assert_eq!(e, "let _ = !v"); - let e = write_expression(&StructWithUnary::type_def().functions()[0].expressions[4]); - assert_eq!(e, "let _ = *y"); - } - - #[test] - fn while_loop() { - #[derive(agdb::TypeDef)] - struct StructWithWhileLoop; - - #[agdb::impl_def()] - #[allow(dead_code)] - impl StructWithWhileLoop { - fn while_example() { - let mut i = 0; - while i < 5 { - i += 1; - } - } - } - - let e = write_expression(&StructWithWhileLoop::type_def().functions()[0].expressions[1]); - assert_eq!(e, "while i < 5 {\ni += 1\n}"); - } -} diff --git a/agdb/src/api_def/function_def.rs b/agdb/src/api_def/function_def.rs deleted file mode 100644 index f2357b2d..00000000 --- a/agdb/src/api_def/function_def.rs +++ /dev/null @@ -1,176 +0,0 @@ -use crate::api_def::Generic; -use crate::api_def::NamedType; -use crate::api_def::Type; -use crate::api_def::expression_def::Expression; - -pub struct Function { - pub name: &'static str, - pub generics: &'static [Generic], - pub args: &'static [NamedType], - pub ret: Option Type>, - pub async_fn: bool, - pub expressions: &'static [Expression], -} - -#[cfg(test)] -mod tests { - use crate::api_def::TypeDefinition; - - #[test] - fn simple_function() { - #[derive(agdb::TypeDef)] - struct StructWithFunction; - - #[agdb::impl_def()] - #[allow(dead_code)] - impl StructWithFunction { - fn example_function() {} - } - - let functions = StructWithFunction::type_def().functions(); - - assert_eq!(functions.len(), 1); - assert_eq!(functions[0].name, "example_function"); - assert!(functions[0].generics.is_empty()); - assert!(functions[0].args.is_empty()); - assert!(functions[0].ret.is_none()); - assert!(functions[0].expressions.is_empty()); - } - - #[test] - fn function_with_args_and_return() { - #[derive(agdb::TypeDef)] - struct StructWithFunctionArgs; - - #[agdb::impl_def()] - #[allow(dead_code)] - impl StructWithFunctionArgs { - fn add(a: i32, b: i32) -> i32 { - a + b - } - } - - let functions = StructWithFunctionArgs::type_def().functions(); - - assert_eq!(functions.len(), 1); - let func = &functions[0]; - assert_eq!(func.name, "add"); - assert_eq!(func.args.len(), 2); - assert_eq!(func.args[0].name, "a"); - assert_eq!((func.args[0].ty.unwrap())().name(), "i32"); - assert_eq!(func.args[1].name, "b"); - assert_eq!((func.args[1].ty.unwrap())().name(), "i32"); - assert!(func.ret.is_some()); - } - - #[test] - fn function_with_generics() { - #[derive(agdb::TypeDef)] - struct StructWithGenericFunction; - - #[agdb::impl_def()] - #[allow(dead_code)] - impl StructWithGenericFunction { - fn identity(value: T) -> T { - value - } - } - - let functions = StructWithGenericFunction::type_def().functions(); - - assert_eq!(functions.len(), 1); - let func = &functions[0]; - assert_eq!(func.name, "identity"); - assert_eq!(func.generics.len(), 1); - assert_eq!(func.generics[0].name, "T"); - assert_eq!(func.args.len(), 1); - assert_eq!(func.args[0].name, "value"); - assert_eq!((func.args[0].ty.unwrap())().name(), "T"); - assert!(func.ret.is_some()); - } - - #[test] - fn impl_with_generics() { - #[derive(agdb::TypeDef)] - #[allow(dead_code)] - struct StructWithGenericImpl { - value: T, - } - - #[agdb::impl_def()] - #[allow(dead_code)] - impl StructWithGenericImpl { - fn generic_method(value: T) -> T { - value - } - } - - let functions = StructWithGenericImpl::::type_def().functions(); - - assert_eq!(functions.len(), 1); - let func = &functions[0]; - assert_eq!(func.name, "generic_method"); - assert_eq!(func.generics.len(), 0); - assert_eq!(func.args.len(), 1); - assert_eq!(func.args[0].name, "value"); - assert_eq!((func.args[0].ty.unwrap())().name(), "T"); - assert!(func.ret.is_some()); - } - - #[test] - fn impl_with_generics_and_bounds() { - trait Debuggable {} - - impl Debuggable for i32 {} - - #[derive(agdb::TypeDef)] - #[allow(dead_code)] - struct StructWithBoundedGenericImpl { - value: T, - } - - #[agdb::impl_def()] - #[allow(dead_code)] - impl StructWithBoundedGenericImpl - where - T: Debuggable, - { - fn debug_value(value: T) -> T { - value - } - } - - let functions = StructWithBoundedGenericImpl::::type_def().functions(); - - assert_eq!(functions.len(), 1); - let func = &functions[0]; - assert_eq!(func.name, "debug_value"); - assert_eq!(func.generics.len(), 0); - assert_eq!(func.args.len(), 1); - assert_eq!(func.args[0].name, "value"); - assert_eq!((func.args[0].ty.unwrap())().name(), "T"); - assert!(func.ret.is_some()); - } - - #[test] - fn async_function() { - #[derive(agdb::TypeDef)] - struct StructWithAsync; - - #[agdb::impl_def()] - #[allow(dead_code)] - impl StructWithAsync { - async fn async_function() {} - } - - let functions = StructWithAsync::type_def().functions(); - - assert_eq!(functions.len(), 1); - assert_eq!(functions[0].name, "async_function"); - assert!(functions[0].generics.is_empty()); - assert!(functions[0].args.is_empty()); - assert!(functions[0].ret.is_none()); - assert!(functions[0].async_fn); - assert!(functions[0].expressions.is_empty()); - } -} diff --git a/agdb/src/api_def/struct_def.rs b/agdb/src/api_def/struct_def.rs deleted file mode 100644 index 7a110daf..00000000 --- a/agdb/src/api_def/struct_def.rs +++ /dev/null @@ -1,101 +0,0 @@ -use crate::api_def::Function; -use crate::api_def::Generic; -use crate::api_def::NamedType; - -pub struct Struct { - pub name: &'static str, - pub generics: &'static [Generic], - pub fields: &'static [NamedType], - pub functions: &'static [Function], -} - -#[cfg(test)] -mod tests { - use crate::api_def::Type; - use crate::api_def::TypeDefinition; - - #[test] - fn struct_definition() { - #[derive(agdb::TypeDefImpl)] - struct SomeStruct; - - let struct_def = SomeStruct::type_def(); - - if let Type::Struct(s) = struct_def { - assert_eq!(s.name, "SomeStruct"); - assert_eq!(s.generics.len(), 0); - assert_eq!(s.fields.len(), 0); - } else { - panic!("Expected Type::Struct"); - } - } - - #[test] - fn struct_definition_with_generics() { - #[derive(agdb::TypeDefImpl)] - struct GenericStruct { - #[allow(dead_code)] - field: T, - } - - let struct_def = GenericStruct::::type_def(); - - if let Type::Struct(s) = struct_def { - assert_eq!(s.generics.len(), 1); - assert_eq!(s.generics[0].name, "T"); - assert_eq!(s.generics[0].bounds.len(), 0); - assert_eq!(s.fields.len(), 1); - assert_eq!(s.fields[0].name, "field"); - } else { - panic!("Expected Type::Struct"); - } - } - - #[test] - fn struct_definition_with_generics_with_bounds() { - trait Bound5 {} - - #[derive(agdb::TypeDefImpl)] - struct GenericStruct - where - T: Bound5, - { - #[allow(dead_code)] - field: T, - } - - struct BoundedStruct; - impl Bound5 for BoundedStruct {} - - let struct_def = GenericStruct::::type_def(); - - if let Type::Struct(s) = struct_def { - assert_eq!(s.generics.len(), 1); - assert_eq!(s.generics[0].name, "T"); - assert_eq!(s.generics[0].bounds.len(), 1); - assert_eq!(s.generics[0].bounds[0], "Bound5"); - } else { - panic!("Expected Type::Struct"); - } - } - - #[test] - fn struct_definition_with_fields() { - #[derive(agdb::TypeDefImpl)] - #[allow(dead_code)] - struct FieldStruct { - a: i32, - b: String, - } - - let struct_def = FieldStruct::type_def(); - - if let Type::Struct(s) = struct_def { - assert_eq!(s.fields.len(), 2); - assert_eq!(s.fields[0].name, "a"); - assert_eq!(s.fields[1].name, "b"); - } else { - panic!("Expected Type::Struct"); - } - } -} diff --git a/agdb/src/api_def/tuple_def.rs b/agdb/src/api_def/tuple_def.rs deleted file mode 100644 index 36a1d4d8..00000000 --- a/agdb/src/api_def/tuple_def.rs +++ /dev/null @@ -1,100 +0,0 @@ -use crate::api_def::Function; -use crate::api_def::Generic; -use crate::api_def::Type; - -pub struct Tuple { - pub name: &'static str, - pub generics: &'static [Generic], - pub fields: &'static [fn() -> Type], - pub functions: &'static [Function], -} - -#[cfg(test)] -mod tests { - use crate::api_def::Type; - use crate::api_def::TypeDefinition; - - #[test] - fn tuple_definition() { - #[derive(agdb::TypeDefImpl)] - struct SomeTuple(); - - let tuple_def = SomeTuple::type_def(); - - if let Type::Tuple(t) = tuple_def { - assert_eq!(t.name, "SomeTuple"); - assert_eq!(t.generics.len(), 0); - assert_eq!(t.fields.len(), 0); - assert_eq!(t.functions.len(), 0); - } else { - panic!("Expected Type::Tuple"); - } - } - - #[test] - fn tuple_definition_with_generics() { - #[derive(agdb::TypeDefImpl)] - struct GenericTuple(T); - - let tuple_def = GenericTuple::::type_def(); - - if let Type::Tuple(t) = tuple_def { - assert_eq!(t.generics.len(), 1); - assert_eq!(t.generics[0].name, "T"); - assert_eq!(t.generics[0].bounds.len(), 0); - assert_eq!(t.fields.len(), 1); - if let Type::Struct(s) = (t.fields[0])() { - assert_eq!(s.name, "T"); - } else { - panic!("Expected field type to be T Struct"); - } - } else { - panic!("Expected Type::Tuple"); - } - } - - #[test] - fn tuple_definition_with_concrete_type() { - #[derive(agdb::TypeDefImpl)] - #[allow(dead_code)] - struct ConcreteTuple(i32); - - let tuple_def = ConcreteTuple::type_def(); - - if let Type::Tuple(t) = tuple_def { - assert_eq!(t.fields.len(), 1); - if let Type::Struct(s) = (t.fields[0])() { - assert_eq!(s.name, "i32"); - } else { - panic!("Expected field type to be T Struct"); - } - } else { - panic!("Expected Type::Tuple"); - } - } - - #[test] - fn tuple_definition_with_multiple_fields() { - #[derive(agdb::TypeDefImpl)] - #[allow(dead_code)] - struct ConcreteTuple(i32, String); - - let tuple_def = ConcreteTuple::type_def(); - - if let Type::Tuple(t) = tuple_def { - assert_eq!(t.fields.len(), 2); - if let Type::Struct(s) = (t.fields[0])() { - assert_eq!(s.name, "i32"); - } else { - panic!("Expected field type to be T Struct"); - } - if let Type::Struct(s) = (t.fields[1])() { - assert_eq!(s.name, "String"); - } else { - panic!("Expected field type to be T Struct"); - } - } else { - panic!("Expected Type::Tuple"); - } - } -} diff --git a/agdb/src/lib.rs b/agdb/src/lib.rs index 4412098e..7bac028d 100644 --- a/agdb/src/lib.rs +++ b/agdb/src/lib.rs @@ -43,9 +43,9 @@ pub use query::QueryType; pub use agdb_derive::{DbElement, DbSerialize, DbType, DbTypeMarker, DbValue}; #[cfg(feature = "api")] -pub mod api_def; +pub mod type_def; #[cfg(feature = "api")] -pub use agdb_derive::{TypeDef, TypeDefImpl, impl_def}; +pub use agdb_derive::{TypeDef, TypeDefImpl, fn_def, impl_def, trait_def}; pub use db::Db; pub use db::DbAny; diff --git a/agdb/src/query_builder/search.rs b/agdb/src/query_builder/search.rs index 30c62428..c6aa6a86 100644 --- a/agdb/src/query_builder/search.rs +++ b/agdb/src/query_builder/search.rs @@ -12,7 +12,8 @@ use crate::db::db_key_order::DbKeyOrders; use crate::query::query_condition::KeyValueComparison; #[cfg(feature = "api")] -pub trait SearchQueryBuilder: agdb::api_def::TypeDefinition { +#[cfg_attr(feature = "api", agdb::trait_def())] +pub trait SearchQueryBuilder: agdb::type_def::TypeDefinition { fn search_mut(&mut self) -> &mut SearchQuery; } diff --git a/agdb/src/type_def.rs b/agdb/src/type_def.rs new file mode 100644 index 00000000..b65e37c2 --- /dev/null +++ b/agdb/src/type_def.rs @@ -0,0 +1,405 @@ +pub mod enum_def; +pub mod expression_def; +pub mod function_def; +pub mod impl_def; +pub mod struct_def; +pub mod trait_def; + +pub use enum_def::Enum; +pub use expression_def::Expression; +pub use expression_def::LiteralValue; +pub use expression_def::Op; +pub use function_def::Function; +pub use impl_def::Impl; +pub use struct_def::Struct; +pub use trait_def::Trait; + +pub type Tuple = &'static [fn() -> Type]; + +pub trait TypeDefinition { + fn type_def() -> Type; + + fn generic_type_names() -> Vec<&'static str> { + match Self::type_def() { + Type::Enum(e) => e.generics.iter().map(|g| g.name).collect(), + Type::Function(f) => f.generics.iter().map(|g| g.name).collect(), + Type::Struct(s) => s.generics.iter().map(|g| g.name).collect(), + Type::Trait(t) => t.generics.iter().map(|g| g.name).collect(), + Type::Impl(i) => i.generics.iter().map(|g| g.name).collect(), + _ => vec![], + } + } +} + +pub trait ImplDefinition: TypeDefinition { + fn impl_def() -> Impl { + Impl { + name: Self::type_def().name(), + generics: &[], + trait_: None, + ty: Self::type_def, + functions: Self::functions(), + } + } + + fn functions() -> &'static [Function] { + &[] + } +} + +#[derive(Debug, agdb::TypeDefImpl)] +pub enum Type { + Enum(Enum), + Function(Function), + Generic(Generic), + Impl(Impl), + Literal(Literal), + Option(fn() -> Type), + Pointer(Pointer), + Reference(Reference), + Result { ok: fn() -> Type, err: fn() -> Type }, + SelfType(bool), + Slice(fn() -> Type), + Struct(Struct), + Trait(Trait), + Tuple(&'static [fn() -> Type]), + Vec(fn() -> Type), +} + +impl Type { + pub fn name(&self) -> &'static str { + match self { + Type::Enum(e) => e.name, + Type::Function(_) => "fn", + Type::Generic(g) => g.name, + Type::Impl(i) => i.name, + Type::Literal(l) => l.name(), + Type::Option(_) => "Option", + Type::Pointer(_) => "Pointer", + Type::Reference(_) => "Reference", + Type::Result { .. } => "Result", + Type::SelfType(_) => "Self", + Type::Slice(_) => "Slice", + Type::Struct(s) => s.name, + Type::Trait(t) => t.name, + Type::Tuple(_) => "Tuple", + Type::Vec(_) => "Vec", + } + } + + #[allow(dead_code)] + pub fn functions(&self) -> &'static [Function] { + match self { + Type::Impl(i) => i.functions, + Type::Trait(t) => t.functions, + _ => &[], + } + } +} + +#[derive(Debug, agdb::TypeDefImpl)] +pub struct Variable { + pub name: &'static str, + pub ty: Option Type>, +} + +#[derive(Debug, agdb::TypeDefImpl)] +pub struct Generic { + pub kind: GenericKind, + pub name: &'static str, + pub bounds: &'static [fn() -> Type], +} + +#[derive(Debug, agdb::TypeDefImpl)] +pub enum GenericKind { + Type, + Lifetime, + Const, +} + +#[derive(Debug, agdb::TypeDefImpl)] +pub struct Reference { + pub mutable: bool, + pub lifetime: Option<&'static str>, + pub ty: fn() -> Type, +} + +#[derive(Debug, agdb::TypeDefImpl)] +pub struct Pointer { + pub kind: PointerKind, + pub ty: fn() -> Type, +} + +#[derive(Debug, agdb::TypeDefImpl)] +pub enum PointerKind { + Arc, + ArcWeak, + Box, + Cell, + Cow, + LazyCell, + LazyLock, + Mutex, + OnceCell, + OnceLock, + Pin, + Raw, + Rc, + RcWeak, + RefCell, + RwLock, + UnsafeCell, +} + +#[derive(Debug, agdb::TypeDefImpl)] +pub enum Literal { + Bool, + F32, + F64, + I8, + I16, + I32, + I64, + Str, + String, + U8, + U16, + U32, + U64, + Unit, + Usize, +} + +impl Literal { + pub const fn name(&self) -> &'static str { + match self { + Literal::Bool => "bool", + Literal::F32 => "f32", + Literal::F64 => "f64", + Literal::I8 => "i8", + Literal::I16 => "i16", + Literal::I32 => "i32", + Literal::I64 => "i64", + Literal::Str => "str", + Literal::String => "String", + Literal::U8 => "u8", + Literal::U16 => "u16", + Literal::U32 => "u32", + Literal::U64 => "u64", + Literal::Unit => "()", + Literal::Usize => "usize", + } + } +} + +macro_rules! impl_type_def_literal { + ($($ty:ty => $variant:ident),* $(,)?) => { + $( + impl TypeDefinition for $ty { + fn type_def() -> Type { + Type::Literal(Literal::$variant) + } + } + impl ImplDefinition for $ty {} + )* + }; +} + +impl_type_def_literal! { + bool => Bool, + i8 => I8, + i16 => I16, + i32 => I32, + i64 => I64, + f32 => F32, + f64 => F64, + &str => Str, + String => String, + u8 => U8, + u16 => U16, + u32 => U32, + u64 => U64, + usize => Usize, + () => Unit, +} + +impl TypeDefinition for &[T] { + fn type_def() -> Type { + Type::Slice(T::type_def) + } +} +impl ImplDefinition for &[T] {} + +impl TypeDefinition for Vec { + fn type_def() -> Type { + Type::Vec(T::type_def) + } +} +impl ImplDefinition for Vec {} + +impl TypeDefinition for Result { + fn type_def() -> Type { + Type::Result { + ok: T::type_def, + err: E::type_def, + } + } +} +impl ImplDefinition for Result {} + +impl TypeDefinition for Option { + fn type_def() -> Type { + Type::Option(T::type_def) + } +} +impl ImplDefinition for Option {} + +impl TypeDefinition for Box { + fn type_def() -> Type { + Type::Pointer(Pointer { + kind: PointerKind::Box, + ty: T::type_def, + }) + } +} +impl ImplDefinition for Box {} + +impl TypeDefinition for &T +where + T: TypeDefinition, +{ + fn type_def() -> Type { + Type::Reference(Reference { + mutable: false, + lifetime: None, + ty: T::type_def, + }) + } +} +impl ImplDefinition for &T where T: TypeDefinition {} + +impl TypeDefinition for (T, V) { + fn type_def() -> Type { + Type::Tuple(&[T::type_def, V::type_def]) + } +} +impl ImplDefinition for (T, V) {} + +macro_rules! impl_type_def_fn_ptr { + ($(($($arg:ident),*)),* $(,)?) => { + $( + impl TypeDefinition for fn($($arg),*) -> R { + fn type_def() -> Type { + Type::Function(Function { + name: "", + generics: &[], + args: &[ + $( + Variable { + name: "", + ty: Some(<$arg as TypeDefinition>::type_def), + } + ),* + ], + ret: ::type_def, + async_fn: false, + body: &[], + }) + } + } + impl ImplDefinition for fn($($arg),*) -> R {} + )* + }; +} + +impl_type_def_fn_ptr! { + (A0), + (A0, A1), + (A0, A1, A2), + (A0, A1, A2, A3), +} + +impl TypeDefinition for fn() -> Type { + fn type_def() -> Type { + Type::Function(Function { + name: "", + generics: &[], + args: &[], + ret: Type::type_def, + async_fn: false, + body: &[], + }) + } +} +impl ImplDefinition for fn() -> Type {} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_type_def_literals() { + assert!(matches!(bool::type_def(), Type::Literal(Literal::Bool))); + assert!(matches!(i32::type_def(), Type::Literal(Literal::I32))); + assert!(matches!(f64::type_def(), Type::Literal(Literal::F64))); + assert!(matches!(String::type_def(), Type::Literal(Literal::String))); + } + + #[test] + fn options() { + let Type::Option(inner) = Option::::type_def() else { + panic!("Expected an option type definition"); + }; + + let Type::Literal(Literal::I32) = inner() else { + panic!("Expected a literal type definition"); + }; + } + + #[test] + fn results() { + let Type::Result { ok, err } = Result::::type_def() else { + panic!("Expected a result type definition"); + }; + + let Type::Literal(Literal::I32) = ok() else { + panic!("Expected a literal type definition for ok"); + }; + + let Type::Literal(Literal::String) = err() else { + panic!("Expected a literal type definition for err"); + }; + } + + #[test] + fn derive_type_enum_itself() { + let Type::Enum(def) = Type::type_def() else { + panic!("Expected enum type definition for Type"); + }; + + assert_eq!(def.name, "Type"); + assert!(def.variants.iter().any(|v| v.name == "Function")); + assert!(def.variants.iter().any(|v| v.name == "Trait")); + } + + #[test] + fn function_pointer_type_def() { + let Type::Function(def) = String as TypeDefinition>::type_def() else { + panic!("Expected function type definition"); + }; + + assert_eq!(def.args.len(), 1); + assert_eq!(def.args[0].name, ""); + + let Some(arg_ty) = def.args[0].ty else { + panic!("Expected i32 argument type"); + }; + let Type::Literal(Literal::I32) = arg_ty() else { + panic!("Expected i32 argument type"); + }; + + let Type::Literal(Literal::String) = (def.ret)() else { + panic!("Expected String return type"); + }; + } +} diff --git a/agdb/src/type_def/enum_def.rs b/agdb/src/type_def/enum_def.rs new file mode 100644 index 00000000..cf533d42 --- /dev/null +++ b/agdb/src/type_def/enum_def.rs @@ -0,0 +1,393 @@ +use crate::type_def::Function; +use crate::type_def::Generic; +use crate::type_def::Variable; + +#[derive(Debug, agdb::TypeDefImpl)] +pub struct Enum { + pub name: &'static str, + pub generics: &'static [Generic], + pub variants: &'static [Variable], + pub functions: &'static [Function], +} + +#[cfg(test)] +mod tests { + use crate::type_def::GenericKind; + use crate::type_def::Literal; + use crate::type_def::Type; + use crate::type_def::TypeDefinition; + + #[test] + fn empty_enum() { + #[derive(agdb::TypeDefImpl)] + enum E {} + + let Type::Enum(e) = E::type_def() else { + panic!("Expected an enum type definition"); + }; + + assert_eq!(e.name, "E"); + } + + #[test] + fn enum_with_variants() { + #[derive(agdb::TypeDefImpl)] + #[allow(dead_code)] + enum E { + A, + B, + } + + let Type::Enum(e) = E::type_def() else { + panic!("Expected an enum type definition"); + }; + + assert_eq!(e.name, "E"); + assert_eq!(e.variants.len(), 2); + assert_eq!(e.variants[0].name, "A"); + assert!( + matches!( + (e.variants[0].ty.expect("expected type function"))(), + Type::Literal(Literal::Unit), + ), + "Got: {:?}", + (e.variants[0].ty.expect("expected type function"))() + ); + assert_eq!(e.variants[1].name, "B"); + assert!( + matches!( + (e.variants[1].ty.expect("expected type function"))(), + Type::Literal(Literal::Unit), + ), + "Got: {:?}", + (e.variants[1].ty.expect("expected type function"))() + ); + } + + #[test] + fn enum_with_typed_variant() { + #[derive(agdb::TypeDefImpl)] + #[allow(dead_code)] + enum E { + A(String), + } + + let Type::Enum(e) = E::type_def() else { + panic!("Expected an enum type definition"); + }; + + assert_eq!(e.name, "E"); + assert_eq!(e.variants.len(), 1); + assert_eq!(e.variants[0].name, "A"); + assert!( + matches!( + (e.variants[0].ty.expect("expected type function"))(), + Type::Literal(Literal::String) + ), + "Got: {:?}", + (e.variants[0].ty.expect("expected type function"))() + ); + } + + #[test] + fn enum_with_struct_variant() { + #[derive(agdb::TypeDefImpl)] + #[allow(dead_code)] + enum E { + A { _a: String }, + } + + let Type::Enum(e) = E::type_def() else { + panic!("Expected an enum type definition"); + }; + + assert_eq!(e.name, "E"); + assert_eq!(e.variants.len(), 1); + assert_eq!(e.variants[0].name, "A"); + let Type::Struct(s) = (e.variants[0].ty.expect("expected type function"))() else { + panic!("Expected a struct type definition"); + }; + assert_eq!(s.fields.len(), 1); + assert_eq!(s.fields[0].name, "_a"); + assert!( + matches!( + (s.fields[0].ty.expect("expected type function"))(), + Type::Literal(Literal::String) + ), + "Got: {:?}", + (s.fields[0].ty.expect("expected type function"))() + ); + } + + #[test] + fn enum_with_tuple_variant() { + #[derive(agdb::TypeDefImpl)] + #[allow(dead_code)] + enum E { + A(String, i32), + } + + let Type::Enum(e) = E::type_def() else { + panic!("Expected an enum type definition"); + }; + + assert_eq!(e.name, "E"); + assert_eq!(e.variants.len(), 1); + assert_eq!(e.variants[0].name, "A"); + let Type::Tuple(fields) = (e.variants[0].ty.expect("expected type function"))() else { + panic!("Expected a tuple type definition"); + }; + assert_eq!(fields.len(), 2); + assert!( + matches!((fields[0])(), Type::Literal(Literal::String)), + "Got: {:?}", + (fields[0])() + ); + assert!( + matches!((fields[1])(), Type::Literal(Literal::I32)), + "Got: {:?}", + (fields[1])() + ); + } + + #[test] + fn generic_enum() { + #[derive(agdb::TypeDefImpl)] + #[allow(dead_code)] + enum E { + A(T), + } + + let Type::Enum(e) = E::::type_def() else { + panic!("Expected an enum type definition"); + }; + + assert_eq!(e.name, "E"); + assert_eq!(e.generics.len(), 1); + assert_eq!(e.generics[0].name, "T"); + assert_eq!(e.generics[0].bounds.len(), 0); + assert_eq!(e.variants.len(), 1); + assert_eq!(e.variants[0].name, "A"); + + let ty = (e.variants[0].ty.expect("expected type function"))(); + + let Type::Generic(generic) = ty else { + panic!("Expected a generic type definition"); + }; + + assert_eq!(generic.name, "T"); + assert_eq!(generic.bounds.len(), 0); + } + + #[test] + fn generic_enum_with_bounds() { + #[agdb::trait_def] + trait MyTrait {} + + impl MyTrait for i32 {} + + #[derive(agdb::TypeDefImpl)] + #[allow(dead_code)] + enum E { + A(T), + } + + let Type::Enum(e) = E::::type_def() else { + panic!("Expected an enum type definition"); + }; + + assert_eq!(e.name, "E"); + assert_eq!(e.generics.len(), 1); + assert_eq!(e.generics[0].name, "T"); + assert_eq!(e.generics[0].bounds.len(), 2); + let Type::Trait(bound_trait) = (e.generics[0].bounds[0])() else { + panic!("Expected a trait type definition"); + }; + + assert_eq!(bound_trait.name, "TypeDefinition"); + + let Type::Trait(bound_trait) = (e.generics[0].bounds[1])() else { + panic!("Expected a trait type definition"); + }; + + assert_eq!(bound_trait.name, "MyTrait"); + + assert_eq!(e.variants.len(), 1); + assert_eq!(e.variants[0].name, "A"); + let ty = (e.variants[0].ty.expect("expected type function"))(); + + let Type::Generic(generic) = ty else { + panic!("Expected a generic type definition"); + }; + + assert_eq!(generic.name, "T"); + assert_eq!(generic.bounds.len(), 2); + let Type::Trait(bound_trait) = (generic.bounds[0])() else { + panic!("Expected a trait type definition"); + }; + + assert_eq!(bound_trait.name, "TypeDefinition"); + + let Type::Trait(bound_trait) = (generic.bounds[1])() else { + panic!("Expected a trait type definition"); + }; + + assert_eq!(bound_trait.name, "MyTrait"); + } + + #[test] + fn generic_enum_with_where_clause() { + #[derive(agdb::TypeDefImpl)] + #[allow(dead_code)] + enum E + where + T: agdb::type_def::TypeDefinition, + { + A(T), + } + + let Type::Enum(e) = E::::type_def() else { + panic!("Expected an enum type definition"); + }; + + assert_eq!(e.name, "E"); + assert_eq!(e.generics.len(), 1); + assert_eq!(e.generics[0].name, "T"); + assert_eq!(e.generics[0].bounds.len(), 1); + let Type::Trait(bound_trait) = (e.generics[0].bounds[0])() else { + panic!("Expected a trait type definition"); + }; + + assert_eq!(bound_trait.name, "TypeDefinition"); + + assert_eq!(e.variants.len(), 1); + assert_eq!(e.variants[0].name, "A"); + let ty = (e.variants[0].ty.expect("expected type function"))(); + + let Type::Generic(generic) = ty else { + panic!("Expected a generic type definition"); + }; + + assert_eq!(generic.name, "T"); + assert_eq!(generic.bounds.len(), 1); + let Type::Trait(bound_trait) = (generic.bounds[0])() else { + panic!("Expected a trait type definition"); + }; + + assert_eq!(bound_trait.name, "TypeDefinition"); + } + + #[test] + fn generic_enum_with_struct_variant() { + #[derive(agdb::TypeDefImpl)] + #[allow(dead_code)] + enum E { + A { _a: T }, + } + + let Type::Enum(e) = E::::type_def() else { + panic!("Expected an enum type definition"); + }; + + assert_eq!(e.name, "E"); + assert_eq!(e.generics.len(), 1); + assert_eq!(e.generics[0].name, "T"); + assert_eq!(e.generics[0].bounds.len(), 0); + assert_eq!(e.variants.len(), 1); + assert_eq!(e.variants[0].name, "A"); + + let Type::Struct(s) = (e.variants[0].ty.expect("expected type function"))() else { + panic!("Expected a struct type definition"); + }; + assert_eq!(s.fields.len(), 1); + assert_eq!(s.fields[0].name, "_a"); + + let Type::Generic(generic) = (s.fields[0].ty.expect("expected type function"))() else { + panic!("Expected a generic type definition"); + }; + + assert_eq!(generic.name, "T"); + assert_eq!(generic.bounds.len(), 0); + } + + #[test] + fn generic_enum_with_tuple_variant_multiple_types() { + #[derive(agdb::TypeDefImpl)] + #[allow(dead_code)] + enum E { + A(T, i32), + } + + let Type::Enum(e) = E::::type_def() else { + panic!("Expected an enum type definition"); + }; + + assert_eq!(e.name, "E"); + assert_eq!(e.generics.len(), 1); + assert_eq!(e.generics[0].name, "T"); + assert_eq!(e.generics[0].bounds.len(), 0); + assert_eq!(e.variants.len(), 1); + assert_eq!(e.variants[0].name, "A"); + + let Type::Tuple(fields) = (e.variants[0].ty.expect("expected type function"))() else { + panic!("Expected a tuple type definition"); + }; + + assert_eq!(fields.len(), 2); + + let Type::Generic(generic) = (fields[0])() else { + panic!("Expected a generic type definition"); + }; + + assert_eq!(generic.name, "T"); + assert_eq!(generic.bounds.len(), 0); + assert!( + matches!((fields[1])(), Type::Literal(Literal::I32)), + "Got: {:?}", + (fields[1])() + ); + } + + #[test] + fn enum_with_lifetime() { + #[derive(agdb::TypeDefImpl)] + #[allow(dead_code)] + enum E<'a> { + A(&'a str), + } + + let Type::Enum(e) = E::type_def() else { + panic!("Expected enum type definition"); + }; + + assert_eq!(e.generics.len(), 1); + assert!(matches!(e.generics[0].kind, GenericKind::Lifetime)); + assert_eq!(e.generics[0].name, "a"); + assert_eq!(e.generics[0].bounds.len(), 0); + assert_eq!(e.name, "E"); + } + + #[test] + fn enum_with_const_generic() { + #[derive(agdb::TypeDefImpl)] + #[allow(dead_code)] + enum E { + A, + } + + let Type::Enum(e) = E::<1>::type_def() else { + panic!("Expected enum type definition"); + }; + + assert_eq!(e.generics.len(), 1); + assert!(matches!(e.generics[0].kind, GenericKind::Const)); + assert_eq!(e.generics[0].name, "N"); + assert_eq!(e.generics[0].bounds.len(), 1); + assert!( + matches!((e.generics[0].bounds[0])(), Type::Literal(Literal::Usize)), + "Got: {:?}", + (e.generics[0].bounds[0])() + ); + assert_eq!(e.name, "E"); + } +} diff --git a/agdb/src/type_def/expression_def.rs b/agdb/src/type_def/expression_def.rs new file mode 100644 index 00000000..733e11bb --- /dev/null +++ b/agdb/src/type_def/expression_def.rs @@ -0,0 +1,1441 @@ +use crate::type_def::Function; +use crate::type_def::Type; + +#[derive(Debug, agdb::TypeDefImpl)] +pub enum Expression { + Array(&'static [Expression]), + Assign { + target: &'static Expression, + value: &'static Expression, + }, + Await(&'static Expression), + Binary { + op: Op, + left: &'static Expression, + right: &'static Expression, + }, + Block(&'static [Expression]), + Break, + Call { + recipient: Option<&'static Expression>, + function: &'static Expression, + args: &'static [Expression], + }, + Closure(Function), + Continue, + FieldAccess { + base: &'static Expression, + field: &'static str, + }, + For { + pattern: &'static Expression, + iterable: &'static Expression, + body: &'static Expression, + }, + Format { + format_string: &'static str, + args: &'static [Expression], + }, + Ident(&'static str), + If { + condition: &'static Expression, + then_branch: &'static Expression, + else_branch: Option<&'static Expression>, + }, + Index { + base: &'static Expression, + index: &'static Expression, + }, + Let { + name: &'static Expression, + ty: Option Type>, + value: Option<&'static Expression>, + }, + Literal(LiteralValue), + Path { + ident: &'static str, + parent: Option<&'static Expression>, + generics: &'static [fn() -> Type], + }, + Reference(&'static Expression), + Return(Option<&'static Expression>), + Struct { + name: &'static Expression, + fields: &'static [(&'static str, Expression)], + }, + StructPattern { + name: &'static Expression, + fields: &'static [Expression], + }, + Try(&'static Expression), + Tuple(&'static [Expression]), + TupleStruct { + name: &'static Expression, + expressions: &'static [Expression], + }, + TupleAccess { + base: &'static Expression, + index: u32, + }, + Unary { + op: Op, + expr: &'static Expression, + }, + While { + condition: &'static Expression, + body: &'static Expression, + }, + Wild, +} + +#[derive(Debug, agdb::TypeDefImpl)] +pub enum LiteralValue { + Bool(bool), + F32(f32), + F64(f64), + I8(i8), + I16(i16), + I32(i32), + I64(i64), + Str(&'static str), + String(String), + U8(u8), + U16(u16), + U32(u32), + U64(u64), + Unit, + Usize(usize), +} + +#[derive(Debug, agdb::TypeDefImpl)] +pub enum Op { + Add, + AddAssign, + Sub, + SubAssign, + Mul, + MulAssign, + Div, + DivAssign, + Rem, + RemAssign, + And, + Or, + BitXor, + BitXorAssign, + BitAnd, + BitAndAssign, + BitOr, + BitOrAssign, + Shl, + ShlAssign, + Shr, + ShrAssign, + Eq, + Lt, + Le, + Ne, + Ge, + Gt, + Not, + Neg, + Deref, +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::type_def::Literal; + use crate::type_def::Type; + + fn get_body(name: &str) -> &'static [Expression] { + let func_type = match name { + "literal_integer" => __literal_integer_type_def(), + "literal_bool" => __literal_bool_type_def(), + "literal_string" => __literal_string_type_def(), + "literal_float" => __literal_float_type_def(), + "literal_suffixed" => __literal_suffixed_type_def(), + "array_expr" => __array_expr_type_def(), + "assign_expr" => __assign_expr_type_def(), + "binary_arithmetic" => __binary_arithmetic_type_def(), + "binary_comparison" => __binary_comparison_type_def(), + "binary_logical" => __binary_logical_type_def(), + "binary_assign_ops" => __binary_assign_ops_type_def(), + "unary_neg" => __unary_neg_type_def(), + "unary_not" => __unary_not_type_def(), + "block_expr" => __block_expr_type_def(), + "break_continue" => __break_continue_type_def(), + "call_function" => __call_function_type_def(), + "call_method" => __call_method_type_def(), + "closure_simple" => __closure_simple_type_def(), + "closure_typed" => __closure_typed_type_def(), + "field_access_expr" => __field_access_expr_type_def(), + "tuple_access_expr" => __tuple_access_expr_type_def(), + "for_loop_expr" => __for_loop_expr_type_def(), + "while_loop_expr" => __while_loop_expr_type_def(), + "loop_expr" => __loop_expr_type_def(), + "if_expr" => __if_expr_type_def(), + "if_else_expr" => __if_else_expr_type_def(), + "if_else_if_expr" => __if_else_if_expr_type_def(), + "index_expr" => __index_expr_type_def(), + "let_simple" => __let_simple_type_def(), + "let_typed" => __let_typed_type_def(), + "let_no_init" => __let_no_init_type_def(), + "reference_expr" => __reference_expr_type_def(), + "return_value" => __return_value_type_def(), + "return_none" => __return_none_type_def(), + "try_expr" => __try_expr_type_def(), + "tuple_expr" => __tuple_expr_type_def(), + "format_expr" => __format_expr_type_def(), + "vec_macro_expr" => __vec_macro_expr_type_def(), + "match_expr" => __match_expr_type_def(), + "struct_expr" => __struct_expr_type_def(), + "implicit_return" => __implicit_return_type_def(), + "ident_expr" => __ident_expr_type_def(), + "wild_expr" => __wild_expr_type_def(), + "path_expr" => __path_expr_type_def(), + "let_pattern_tuple" => __let_pattern_tuple_type_def(), + "for_pattern" => __for_pattern_type_def(), + _ => panic!("Unknown test function: {name}"), + }; + let Type::Function(def) = func_type else { + panic!("Expected function type definition"); + }; + def.body + } + + #[agdb::fn_def] + #[allow(unused)] + fn literal_integer() -> i32 { + 42 + } + + #[test] + fn test_literal_integer() { + let body = get_body("literal_integer"); + assert_eq!(body.len(), 1); + assert!( + matches!( + &body[0], + Expression::Return(Some(Expression::Literal(LiteralValue::I32(42)))) + ), + "Got: {:?}", + body[0] + ); + } + + #[agdb::fn_def] + #[allow(unused)] + fn literal_bool() -> bool { + true + } + + #[test] + fn test_literal_bool() { + let body = get_body("literal_bool"); + assert_eq!(body.len(), 1); + assert!( + matches!( + &body[0], + Expression::Return(Some(Expression::Literal(LiteralValue::Bool(true)))) + ), + "Got: {:?}", + body[0] + ); + } + + #[agdb::fn_def] + #[allow(unused)] + fn literal_string() -> &'static str { + "hello" + } + + #[test] + fn test_literal_string() { + let body = get_body("literal_string"); + assert_eq!(body.len(), 1); + match &body[0] { + Expression::Return(Some(Expression::Literal(LiteralValue::Str(s)))) => { + assert_eq!(*s, "hello"); + } + _ => panic!("Got: {:?}", body[0]), + } + } + + #[agdb::fn_def] + #[allow(unused)] + fn literal_float() -> f64 { + 3.3 + } + + #[test] + fn test_literal_float() { + let body = get_body("literal_float"); + assert_eq!(body.len(), 1); + match &body[0] { + Expression::Return(Some(Expression::Literal(LiteralValue::F64(v)))) => { + assert!((*v - 3.3).abs() < f64::EPSILON); + } + _ => panic!("Got: {:?}", body[0]), + } + } + + #[agdb::fn_def] + #[allow(unused)] + fn literal_suffixed() { + let _a = 1u8; + let _b = 2u16; + let _c = 3u32; + let _d = 4u64; + let _e = 5i8; + let _f = 6i16; + let _g = 7i32; + let _h = 8usize; + let _i = 1.0f32; + } + + #[test] + fn test_literal_suffixed() { + let body = get_body("literal_suffixed"); + assert_eq!(body.len(), 9); + + match &body[0] { + Expression::Let { + value: Some(Expression::Literal(LiteralValue::U8(1))), + .. + } => {} + _ => panic!("Got: {:?}", body[0]), + } + match &body[1] { + Expression::Let { + value: Some(Expression::Literal(LiteralValue::U16(2))), + .. + } => {} + _ => panic!("Got: {:?}", body[1]), + } + match &body[2] { + Expression::Let { + value: Some(Expression::Literal(LiteralValue::U32(3))), + .. + } => {} + _ => panic!("Got: {:?}", body[2]), + } + match &body[3] { + Expression::Let { + value: Some(Expression::Literal(LiteralValue::U64(4))), + .. + } => {} + _ => panic!("Got: {:?}", body[3]), + } + match &body[4] { + Expression::Let { + value: Some(Expression::Literal(LiteralValue::I8(5))), + .. + } => {} + _ => panic!("Got: {:?}", body[4]), + } + match &body[5] { + Expression::Let { + value: Some(Expression::Literal(LiteralValue::I16(6))), + .. + } => {} + _ => panic!("Got: {:?}", body[5]), + } + match &body[6] { + Expression::Let { + value: Some(Expression::Literal(LiteralValue::I32(7))), + .. + } => {} + _ => panic!("Got: {:?}", body[6]), + } + match &body[7] { + Expression::Let { + value: Some(Expression::Literal(LiteralValue::Usize(8))), + .. + } => {} + _ => panic!("Got: {:?}", body[7]), + } + match &body[8] { + Expression::Let { + value: Some(Expression::Literal(LiteralValue::F32(_))), + .. + } => {} + _ => panic!("Got: {:?}", body[8]), + } + } + + #[agdb::fn_def] + #[allow(unused)] + fn array_expr() { + let _arr = [1, 2, 3]; + } + + #[test] + fn test_array() { + let body = get_body("array_expr"); + assert_eq!(body.len(), 1); + match &body[0] { + Expression::Let { + value: Some(Expression::Array(elems)), + .. + } => { + assert_eq!(elems.len(), 3); + assert!(matches!( + elems[0], + Expression::Literal(LiteralValue::I32(1)) + )); + assert!(matches!( + elems[1], + Expression::Literal(LiteralValue::I32(2)) + )); + assert!(matches!( + elems[2], + Expression::Literal(LiteralValue::I32(3)) + )); + } + _ => panic!("Got: {:?}", body[0]), + } + } + + #[agdb::fn_def] + #[allow(unused)] + fn assign_expr() { + let mut x = 1; + x = 2; + } + + #[test] + fn test_assign() { + let body = get_body("assign_expr"); + assert_eq!(body.len(), 2); + match &body[1] { + Expression::Assign { target, value } => { + assert!(matches!(target, Expression::Ident("x"))); + assert!(matches!(value, Expression::Literal(LiteralValue::I32(2)))); + } + _ => panic!("Got: {:?}", body[1]), + } + } + + #[agdb::fn_def] + #[allow(unused)] + fn binary_arithmetic() { + let _a = 1 + 2; + let _b = 3 - 4; + let _c = 5 * 6; + let _d = 7 / 8; + let _e = 19 % 10; + } + + #[test] + fn test_binary_arithmetic() { + let body = get_body("binary_arithmetic"); + assert_eq!(body.len(), 5); + + match &body[0] { + Expression::Let { + value: Some(Expression::Binary { op: Op::Add, .. }), + .. + } => {} + _ => panic!("Got: {:?}", body[0]), + } + match &body[1] { + Expression::Let { + value: Some(Expression::Binary { op: Op::Sub, .. }), + .. + } => {} + _ => panic!("Got: {:?}", body[1]), + } + match &body[2] { + Expression::Let { + value: Some(Expression::Binary { op: Op::Mul, .. }), + .. + } => {} + _ => panic!("Got: {:?}", body[2]), + } + match &body[3] { + Expression::Let { + value: Some(Expression::Binary { op: Op::Div, .. }), + .. + } => {} + _ => panic!("Got: {:?}", body[3]), + } + match &body[4] { + Expression::Let { + value: Some(Expression::Binary { op: Op::Rem, .. }), + .. + } => {} + _ => panic!("Got: {:?}", body[4]), + } + } + + #[agdb::fn_def] + #[allow(unused)] + fn binary_comparison() { + let _a = 1 == 2; + let _b = 1 != 2; + let _c = 1 < 2; + let _d = 1 <= 2; + let _e = 1 > 2; + let _f = 1 >= 2; + } + + #[test] + fn test_binary_comparison() { + let body = get_body("binary_comparison"); + assert_eq!(body.len(), 6); + + let ops = [Op::Eq, Op::Ne, Op::Lt, Op::Le, Op::Gt, Op::Ge]; + for (i, expected_op) in ops.iter().enumerate() { + match &body[i] { + Expression::Let { + value: Some(Expression::Binary { op, .. }), + .. + } => assert!( + std::mem::discriminant(op) == std::mem::discriminant(expected_op), + "body[{i}] expected {:?}, got {:?}", + expected_op, + op + ), + _ => panic!("body[{i}]: {:?}", body[i]), + } + } + } + + #[agdb::fn_def] + #[allow(unused, clippy::nonminimal_bool)] + fn binary_logical() { + let _a = true && false; + let _b = true || false; + } + + #[test] + fn test_binary_logical() { + let body = get_body("binary_logical"); + assert_eq!(body.len(), 2); + + match &body[0] { + Expression::Let { + value: Some(Expression::Binary { op: Op::And, .. }), + .. + } => {} + _ => panic!("Got: {:?}", body[0]), + } + match &body[1] { + Expression::Let { + value: Some(Expression::Binary { op: Op::Or, .. }), + .. + } => {} + _ => panic!("Got: {:?}", body[1]), + } + } + + #[agdb::fn_def] + #[allow(unused)] + fn binary_assign_ops() { + let mut x = 0; + x += 1; + x -= 1; + x *= 2; + x /= 2; + x %= 3; + } + + #[test] + fn test_binary_assign_ops() { + let body = get_body("binary_assign_ops"); + assert_eq!(body.len(), 6); + + let ops = [ + Op::AddAssign, + Op::SubAssign, + Op::MulAssign, + Op::DivAssign, + Op::RemAssign, + ]; + for (i, expected_op) in ops.iter().enumerate() { + match &body[i + 1] { + Expression::Binary { op, .. } => assert!( + std::mem::discriminant(op) == std::mem::discriminant(expected_op), + "body[{}] expected {:?}, got {:?}", + i + 1, + expected_op, + op + ), + _ => panic!("body[{}]: {:?}", i + 1, body[i + 1]), + } + } + } + + #[agdb::fn_def] + #[allow(unused)] + fn unary_neg() -> i32 { + -5 + } + + #[test] + fn test_unary_neg() { + let body = get_body("unary_neg"); + assert_eq!(body.len(), 1); + match &body[0] { + Expression::Return(Some(Expression::Unary { op: Op::Neg, .. })) => {} + _ => panic!("Got: {:?}", body[0]), + } + } + + #[agdb::fn_def] + #[allow(unused, clippy::nonminimal_bool)] + fn unary_not() -> bool { + !true + } + + #[test] + fn test_unary_not() { + let body = get_body("unary_not"); + assert_eq!(body.len(), 1); + match &body[0] { + Expression::Return(Some(Expression::Unary { op: Op::Not, .. })) => {} + _ => panic!("Got: {:?}", body[0]), + } + } + + #[agdb::fn_def] + #[allow(unused)] + fn block_expr() { + { + let _x = 1; + }; + } + + #[test] + fn test_block() { + let body = get_body("block_expr"); + assert_eq!(body.len(), 1); + match &body[0] { + Expression::Block(stmts) => { + assert_eq!(stmts.len(), 1); + assert!(matches!(stmts[0], Expression::Let { .. })); + } + _ => panic!("Got: {:?}", body[0]), + } + } + + #[agdb::fn_def] + #[allow(unused, clippy::never_loop)] + fn break_continue() { + loop { + break; + } + loop { + continue; + } + } + + #[test] + fn test_break_continue() { + let body = get_body("break_continue"); + assert_eq!(body.len(), 2); + match &body[0] { + Expression::While { body, .. } => match body { + Expression::Block(stmts) => { + assert!(matches!(stmts[0], Expression::Break)); + } + _ => panic!("Expected block, got {:?}", body), + }, + _ => panic!("Got: {:?}", body[0]), + } + match &body[1] { + Expression::While { body, .. } => match body { + Expression::Block(stmts) => { + assert!(matches!(stmts[0], Expression::Continue)); + } + _ => panic!("Expected block, got {:?}", body), + }, + _ => panic!("Got: {:?}", body[1]), + } + } + + #[agdb::fn_def] + #[allow(unused)] + fn call_function() { + fn helper(_x: i32) -> i32 { + 0 + } + let _r = helper(42); + } + + #[test] + fn test_call_function() { + let body = get_body("call_function"); + assert_eq!(body.len(), 2); + match &body[1] { + Expression::Let { + value: + Some(Expression::Call { + recipient: None, + function, + args, + }), + .. + } => { + assert!( + matches!(function, Expression::Ident("helper")), + "Got function: {:?}", + function + ); + assert_eq!(args.len(), 1); + } + _ => panic!("Got: {:?}", body[1]), + } + } + + #[agdb::fn_def] + #[allow(unused)] + fn call_method() { + let v = [1, 2, 3]; + let _len = v.len(); + } + + #[test] + fn test_call_method() { + let body = get_body("call_method"); + assert_eq!(body.len(), 2); + match &body[1] { + Expression::Let { + value: + Some(Expression::Call { + recipient: Some(_), + function, + args, + }), + .. + } => { + assert!( + matches!(function, Expression::Path { ident: "len", .. }), + "Got function: {:?}", + function + ); + assert_eq!(args.len(), 0); + } + _ => panic!("Got: {:?}", body[1]), + } + } + + #[agdb::fn_def] + #[allow(unused)] + fn closure_simple() { + let _f = |x: i32| x; + } + + #[test] + fn test_closure_simple() { + let body = get_body("closure_simple"); + assert_eq!(body.len(), 1); + match &body[0] { + Expression::Let { + value: Some(Expression::Closure(func)), + .. + } => { + assert_eq!(func.name, ""); + assert_eq!(func.args.len(), 1); + assert_eq!(func.args[0].name, "x"); + } + _ => panic!("Got: {:?}", body[0]), + } + } + + #[agdb::fn_def] + #[allow(unused)] + fn closure_typed() { + let _f = |x: i32| -> i32 { x + 1 }; + } + + #[test] + fn test_closure_typed() { + let body = get_body("closure_typed"); + assert_eq!(body.len(), 1); + match &body[0] { + Expression::Let { + value: Some(Expression::Closure(func)), + .. + } => { + assert_eq!(func.args.len(), 1); + assert_eq!(func.args[0].name, "x"); + assert!(!func.body.is_empty()); + } + _ => panic!("Got: {:?}", body[0]), + } + } + + #[agdb::fn_def] + #[allow(unused)] + fn field_access_expr() { + struct S { + field: i32, + } + let s = S { field: 1 }; + let _f = s.field; + } + + #[test] + fn test_field_access() { + let body = get_body("field_access_expr"); + assert_eq!(body.len(), 3); + match &body[2] { + Expression::Let { + value: + Some(Expression::FieldAccess { + base, + field: "field", + }), + .. + } => { + assert!( + matches!(base, Expression::Ident("s")), + "Got base: {:?}", + base + ); + } + _ => panic!("Got: {:?}", body[2]), + } + } + + #[agdb::fn_def] + #[allow(unused)] + fn tuple_access_expr() { + let t = (1, 2); + let _first = t.0; + } + + #[test] + fn test_tuple_access() { + let body = get_body("tuple_access_expr"); + assert_eq!(body.len(), 2); + match &body[1] { + Expression::Let { + value: Some(Expression::TupleAccess { base, index: 0 }), + .. + } => { + assert!( + matches!(base, Expression::Ident("t")), + "Got base: {:?}", + base + ); + } + _ => panic!("Got: {:?}", body[1]), + } + } + + #[agdb::fn_def] + #[allow(unused)] + fn for_loop_expr() { + let items = [1, 2, 3]; + for _item in items { + let _x = 1; + } + } + + #[test] + fn test_for_loop() { + let body = get_body("for_loop_expr"); + assert_eq!(body.len(), 2); + match &body[1] { + Expression::For { + pattern, + iterable, + body, + } => { + assert!( + matches!(pattern, Expression::Ident("_item")), + "Got pattern: {:?}", + pattern + ); + assert!( + matches!(iterable, Expression::Ident("items")), + "Got iterable: {:?}", + iterable + ); + assert!(matches!(body, Expression::Block(_))); + } + _ => panic!("Got: {:?}", body[1]), + } + } + + #[agdb::fn_def] + #[allow(unused)] + fn while_loop_expr() { + let mut i = 0; + while i < 10 { + i += 1; + } + } + + #[test] + fn test_while_loop() { + let body = get_body("while_loop_expr"); + assert_eq!(body.len(), 2); + match &body[1] { + Expression::While { condition, body } => { + assert!( + matches!(condition, Expression::Binary { op: Op::Lt, .. }), + "Got condition: {:?}", + condition + ); + assert!(matches!(body, Expression::Block(_))); + } + _ => panic!("Got: {:?}", body[1]), + } + } + + #[agdb::fn_def] + #[allow(unused, clippy::never_loop)] + fn loop_expr() { + loop { + break; + } + } + + #[test] + fn test_loop_desugars_to_while_true() { + let body = get_body("loop_expr"); + assert_eq!(body.len(), 1); + match &body[0] { + Expression::While { condition, .. } => { + assert!( + matches!(condition, Expression::Literal(LiteralValue::Bool(true))), + "Got condition: {:?}", + condition + ); + } + _ => panic!("Got: {:?}", body[0]), + } + } + + #[agdb::fn_def] + #[allow(unused)] + fn if_expr() { + if true { + let _x = 1; + } + } + + #[test] + fn test_if() { + let body = get_body("if_expr"); + assert_eq!(body.len(), 1); + match &body[0] { + Expression::If { + condition, + then_branch, + else_branch: None, + } => { + assert!(matches!( + condition, + Expression::Literal(LiteralValue::Bool(true)) + )); + assert!(matches!(then_branch, Expression::Block(_))); + } + _ => panic!("Got: {:?}", body[0]), + } + } + + #[agdb::fn_def] + #[allow(unused)] + fn if_else_expr() { + if true { + let _x = 1; + } else { + let _y = 2; + } + } + + #[test] + fn test_if_else() { + let body = get_body("if_else_expr"); + assert_eq!(body.len(), 1); + match &body[0] { + Expression::If { + else_branch: Some(eb), + .. + } => { + assert!(matches!(eb, Expression::Block(_))); + } + _ => panic!("Got: {:?}", body[0]), + } + } + + #[agdb::fn_def] + #[allow(unused)] + fn if_else_if_expr() { + if true { + let _x = 1; + } else if false { + let _y = 2; + } else { + let _z = 3; + } + } + + #[test] + fn test_if_else_if() { + let body = get_body("if_else_if_expr"); + assert_eq!(body.len(), 1); + match &body[0] { + Expression::If { + else_branch: + Some(Expression::If { + else_branch: Some(_), + .. + }), + .. + } => {} + _ => panic!("Got: {:?}", body[0]), + } + } + + #[agdb::fn_def] + #[allow(unused)] + fn index_expr() { + let arr = [1, 2, 3]; + let _v = arr[0]; + } + + #[test] + fn test_index() { + let body = get_body("index_expr"); + assert_eq!(body.len(), 2); + match &body[1] { + Expression::Let { + value: Some(Expression::Index { base, index }), + .. + } => { + assert!( + matches!(base, Expression::Ident("arr")), + "Got base: {:?}", + base + ); + assert!( + matches!(index, Expression::Literal(LiteralValue::I32(0))), + "Got index: {:?}", + index + ); + } + _ => panic!("Got: {:?}", body[1]), + } + } + + #[agdb::fn_def] + #[allow(unused)] + fn let_simple() { + let _x = 42; + } + + #[test] + fn test_let_simple() { + let body = get_body("let_simple"); + assert_eq!(body.len(), 1); + match &body[0] { + Expression::Let { + name, + ty: None, + value: Some(Expression::Literal(LiteralValue::I32(42))), + } => { + assert!( + matches!(name, Expression::Ident("_x")), + "Got name: {:?}", + name + ); + } + _ => panic!("Got: {:?}", body[0]), + } + } + + #[agdb::fn_def] + #[allow(unused)] + fn let_typed() { + let _x: i32 = 42; + } + + #[test] + fn test_let_typed() { + let body = get_body("let_typed"); + assert_eq!(body.len(), 1); + match &body[0] { + Expression::Let { + name, + ty: Some(ty_fn), + value: Some(_), + } => { + assert!(matches!(name, Expression::Ident("_x"))); + let ty = ty_fn(); + assert!( + matches!(ty, Type::Literal(Literal::I32)), + "Got type: {:?}", + ty + ); + } + _ => panic!("Got: {:?}", body[0]), + } + } + + #[agdb::fn_def] + #[allow(unused)] + fn let_no_init() { + let _x: i32; + } + + #[test] + fn test_let_no_init() { + let body = get_body("let_no_init"); + assert_eq!(body.len(), 1); + match &body[0] { + Expression::Let { + value: None, + ty: Some(_), + .. + } => {} + _ => panic!("Got: {:?}", body[0]), + } + } + + #[agdb::fn_def] + #[allow(unused)] + fn reference_expr() { + let x = 42; + let _r = &x; + } + + #[test] + fn test_reference() { + let body = get_body("reference_expr"); + assert_eq!(body.len(), 2); + match &body[1] { + Expression::Let { + value: Some(Expression::Reference(inner)), + .. + } => { + assert!(matches!(inner, Expression::Ident("x")), "Got: {:?}", inner); + } + _ => panic!("Got: {:?}", body[1]), + } + } + + #[agdb::fn_def] + #[allow(unused, clippy::needless_return)] + fn return_value() -> i32 { + return 42; + } + + #[test] + fn test_return_value() { + let body = get_body("return_value"); + assert_eq!(body.len(), 1); + match &body[0] { + Expression::Return(Some(Expression::Literal(LiteralValue::I32(42)))) => {} + _ => panic!("Got: {:?}", body[0]), + } + } + + #[agdb::fn_def] + #[allow(unused, clippy::needless_return)] + fn return_none() { + return; + } + + #[test] + fn test_return_none() { + let body = get_body("return_none"); + assert_eq!(body.len(), 1); + assert!( + matches!(&body[0], Expression::Return(None)), + "Got: {:?}", + body[0] + ); + } + + #[agdb::fn_def] + #[allow(unused)] + fn try_expr() -> Result { + let r: Result = Ok(1); + r?; + Ok(0) + } + + #[test] + fn test_try() { + let body = get_body("try_expr"); + assert_eq!(body.len(), 3); + match &body[1] { + Expression::Try(inner) => { + assert!(matches!(inner, Expression::Ident("r")), "Got: {:?}", inner); + } + _ => panic!("Got: {:?}", body[1]), + } + } + + #[agdb::fn_def] + #[allow(unused)] + fn tuple_expr() { + let _t = (1, 2, 3); + } + + #[test] + fn test_tuple() { + let body = get_body("tuple_expr"); + assert_eq!(body.len(), 1); + match &body[0] { + Expression::Let { + value: Some(Expression::Tuple(elems)), + .. + } => { + assert_eq!(elems.len(), 3); + } + _ => panic!("Got: {:?}", body[0]), + } + } + + #[agdb::fn_def] + #[allow(unused)] + fn format_expr() { + let x = 42; + let _s = format!("{}", x); + } + + #[test] + fn test_format_macro() { + let body = get_body("format_expr"); + assert_eq!(body.len(), 2); + match &body[1] { + Expression::Let { + value: + Some(Expression::Format { + format_string, + args, + }), + .. + } => { + assert_eq!(*format_string, "{}"); + assert_eq!(args.len(), 1); + assert!( + matches!(args[0], Expression::Ident("x")), + "Got arg: {:?}", + args[0] + ); + } + _ => panic!("Got: {:?}", body[1]), + } + } + + #[agdb::fn_def] + #[allow(unused, clippy::useless_vec)] + fn vec_macro_expr() { + let _v = vec![1, 2, 3]; + } + + #[test] + fn test_vec_macro() { + let body = get_body("vec_macro_expr"); + assert_eq!(body.len(), 1); + match &body[0] { + Expression::Let { + value: Some(Expression::Array(elems)), + .. + } => { + assert_eq!(elems.len(), 3); + } + _ => panic!("Got: {:?}", body[0]), + } + } + + #[agdb::fn_def] + #[allow(unused)] + fn match_expr() -> i32 { + let x = 1; + match x { + 1 => 10, + 2 => 20, + _ => 0, + } + } + + #[test] + fn test_match() { + let body = get_body("match_expr"); + assert_eq!(body.len(), 2); + match &body[1] { + Expression::If { + condition, + then_branch, + else_branch: Some(_), + } => { + assert!( + matches!(condition, Expression::Binary { op: Op::Eq, .. }), + "Got condition: {:?}", + condition + ); + assert!( + matches!(then_branch, Expression::Block(_)), + "Got then: {:?}", + then_branch + ); + } + _ => panic!("Got: {:?}", body[1]), + } + } + + #[agdb::fn_def] + #[allow(unused)] + fn struct_expr() { + struct Point { + x: i32, + y: i32, + } + let _p = Point { x: 1, y: 2 }; + } + + #[test] + fn test_struct_expr() { + let body = get_body("struct_expr"); + assert_eq!(body.len(), 2); + match &body[1] { + Expression::Let { + value: Some(Expression::Struct { name, fields }), + .. + } => { + assert!( + matches!(name, Expression::Ident("Point")), + "Got name: {:?}", + name + ); + assert_eq!(fields.len(), 2); + assert_eq!(fields[0].0, "x"); + assert_eq!(fields[1].0, "y"); + } + _ => panic!("Got: {:?}", body[1]), + } + } + + #[agdb::fn_def] + #[allow(unused, clippy::let_and_return)] + fn implicit_return() -> i32 { + let x = 5; + x + } + + #[test] + fn test_implicit_return() { + let body = get_body("implicit_return"); + assert_eq!(body.len(), 2); + match &body[1] { + Expression::Return(Some(Expression::Ident("x"))) => {} + _ => panic!("Got: {:?}", body[1]), + } + } + + #[agdb::fn_def] + #[allow(unused)] + fn ident_expr() { + let x = 1; + let _y = x; + } + + #[test] + fn test_ident() { + let body = get_body("ident_expr"); + assert_eq!(body.len(), 2); + match &body[1] { + Expression::Let { + value: Some(Expression::Ident("x")), + .. + } => {} + _ => panic!("Got: {:?}", body[1]), + } + } + + #[agdb::fn_def] + #[allow(unused)] + fn wild_expr() { + let _ = 42; + } + + #[test] + fn test_wild() { + let body = get_body("wild_expr"); + assert_eq!(body.len(), 1); + match &body[0] { + Expression::Let { + name: Expression::Wild, + .. + } => {} + _ => panic!("Got: {:?}", body[0]), + } + } + + #[agdb::fn_def] + #[allow(unused)] + fn path_expr() { + let _v: Option = None; + } + + #[test] + fn test_path() { + let body = get_body("path_expr"); + assert_eq!(body.len(), 1); + match &body[0] { + Expression::Let { + value: Some(Expression::Ident("None")), + .. + } => {} + _ => panic!("Got: {:?}", body[0]), + } + } + + #[agdb::fn_def] + #[allow(unused)] + fn let_pattern_tuple() { + let (a, b) = (1, 2); + let _ = a + b; + } + + #[test] + fn test_let_pattern_tuple() { + let body = get_body("let_pattern_tuple"); + assert_eq!(body.len(), 2); + match &body[0] { + Expression::Let { + name: Expression::Tuple(elems), + .. + } => { + assert_eq!(elems.len(), 2); + assert!(matches!(elems[0], Expression::Ident("a"))); + assert!(matches!(elems[1], Expression::Ident("b"))); + } + _ => panic!("Got: {:?}", body[0]), + } + } + + #[agdb::fn_def] + #[allow(unused)] + fn for_pattern() { + let items = [(1, 2), (3, 4)]; + for (a, _b) in items { + let _ = a; + } + } + + #[test] + fn test_for_pattern() { + let body = get_body("for_pattern"); + assert_eq!(body.len(), 2); + match &body[1] { + Expression::For { pattern, .. } => { + assert!( + matches!(pattern, Expression::Tuple(_)), + "Got pattern: {:?}", + pattern + ); + } + _ => panic!("Got: {:?}", body[1]), + } + } +} diff --git a/agdb/src/type_def/function_def.rs b/agdb/src/type_def/function_def.rs new file mode 100644 index 00000000..c2ef91d6 --- /dev/null +++ b/agdb/src/type_def/function_def.rs @@ -0,0 +1,197 @@ +use crate::type_def::Generic; +use crate::type_def::Type; +use crate::type_def::Variable; +use crate::type_def::expression_def::Expression; + +#[derive(Debug, agdb::TypeDefImpl)] +pub struct Function { + pub name: &'static str, + pub generics: &'static [Generic], + pub args: &'static [Variable], + pub ret: fn() -> Type, + pub async_fn: bool, + pub body: &'static [Expression], +} + +#[cfg(test)] +mod tests { + use crate::type_def::GenericKind; + use crate::type_def::Literal; + use crate::type_def::Type; + + #[test] + fn empty_function() { + #[agdb::fn_def] + #[allow(dead_code)] + fn my_function() {} + + let Type::Function(def) = __my_function_type_def() else { + panic!("Expected a function type definition"); + }; + + assert_eq!(def.name, "my_function"); + assert_eq!(def.args.len(), 0); + assert!( + matches!((def.ret)(), Type::Literal(Literal::Unit),), + "Got: {:?}", + (def.ret)() + ); + } + + #[test] + fn function_with_arguments() { + #[agdb::fn_def] + #[allow(dead_code, unused_variables)] + fn my_function(a: i32, b: String) {} + + let Type::Function(def) = __my_function_type_def() else { + panic!("Expected a function type definition"); + }; + + assert_eq!(def.name, "my_function"); + assert_eq!(def.args.len(), 2); + assert_eq!(def.args[0].name, "a"); + assert_eq!(def.args[1].name, "b"); + assert!( + matches!( + (def.args[0].ty.expect("expected type function"))(), + Type::Literal(Literal::I32), + ), + "Got: {:?}", + (def.args[0].ty.expect("expected type function"))() + ); + assert!( + matches!( + (def.args[1].ty.expect("expected type function"))(), + Type::Literal(Literal::String), + ), + "Got: {:?}", + (def.args[1].ty.expect("expected type function"))() + ); + } + + #[test] + fn generic_function_argument_and_return() { + #[agdb::fn_def] + #[allow(dead_code, unused_variables)] + fn my_function(value: T) -> T { + panic!("body should not be used by fn_def parser") + } + + let Type::Function(def) = __my_function_type_def() else { + panic!("Expected a function type definition"); + }; + + assert_eq!(def.name, "my_function"); + assert_eq!(def.generics.len(), 1); + assert_eq!(def.generics[0].name, "T"); + assert_eq!(def.generics[0].bounds.len(), 1); + + let Type::Trait(bound_trait) = (def.generics[0].bounds[0])() else { + panic!("Expected a trait type definition"); + }; + assert_eq!(bound_trait.name, "TypeDefinition"); + + assert_eq!(def.args.len(), 1); + assert_eq!(def.args[0].name, "value"); + + let Type::Generic(arg_generic) = (def.args[0].ty.expect("expected type function"))() else { + panic!("Expected a generic type definition"); + }; + assert_eq!(arg_generic.name, "T"); + assert_eq!(arg_generic.bounds.len(), 1); + + let Type::Generic(ret_generic) = (def.ret)() else { + panic!("Expected a generic return type definition"); + }; + assert_eq!(ret_generic.name, "T"); + assert_eq!(ret_generic.bounds.len(), 1); + } + + #[test] + fn function_with_lifetime() { + #[agdb::fn_def] + #[allow(dead_code, clippy::needless_lifetimes)] + fn borrow<'a>(s: &'a str) -> &'a str { + s + } + + let Type::Function(def) = __borrow_type_def() else { + panic!("Expected function type definition"); + }; + + assert_eq!(def.generics.len(), 1); + assert!(matches!(def.generics[0].kind, GenericKind::Lifetime)); + assert_eq!(def.generics[0].name, "a"); + assert_eq!(def.generics[0].bounds.len(), 0); + assert_eq!(def.name, "borrow"); + assert_eq!(def.args.len(), 1); + assert_eq!(def.args[0].name, "s"); + assert!( + matches!( + (def.args[0].ty.expect("expected type function"))(), + Type::Reference(crate::type_def::Reference { + mutable: false, + lifetime: Some("a"), + ty: _ + }), + ), + "Got: {:?}", + (def.args[0].ty.expect("expected type function"))() + ); + } + + #[test] + fn function_with_args_with_lifetime() { + #[agdb::fn_def] + #[allow(dead_code, clippy::needless_lifetimes)] + fn borrow<'a>(s: &'a mut Vec) -> &'a Vec { + s + } + + let Type::Function(def) = __borrow_type_def() else { + panic!("Expected function type definition"); + }; + + assert_eq!(def.generics.len(), 1); + assert!(matches!(def.generics[0].kind, GenericKind::Lifetime)); + assert_eq!(def.generics[0].name, "a"); + assert_eq!(def.generics[0].bounds.len(), 0); + assert_eq!(def.name, "borrow"); + assert_eq!(def.args.len(), 1); + assert_eq!(def.args[0].name, "s"); + assert!( + matches!( + (def.args[0].ty.expect("expected type function"))(), + Type::Reference(crate::type_def::Reference { + mutable: true, + lifetime: Some("a"), + ty: _ + }), + ), + "Got: {:?}", + (def.args[0].ty.expect("expected type function"))() + ); + } + + #[test] + fn function_with_const_generic() { + #[agdb::fn_def] + #[allow(dead_code)] + fn with_const() {} + + let Type::Function(def) = __with_const_type_def() else { + panic!("Expected function type definition"); + }; + + assert_eq!(def.generics.len(), 1); + assert!(matches!(def.generics[0].kind, GenericKind::Const)); + assert_eq!(def.generics[0].name, "N"); + assert_eq!(def.generics[0].bounds.len(), 1); + assert!( + matches!((def.generics[0].bounds[0])(), Type::Literal(Literal::Usize)), + "Got: {:?}", + (def.generics[0].bounds[0])() + ); + } +} diff --git a/agdb/src/type_def/impl_def.rs b/agdb/src/type_def/impl_def.rs new file mode 100644 index 00000000..a2cbb45e --- /dev/null +++ b/agdb/src/type_def/impl_def.rs @@ -0,0 +1,209 @@ +use crate::type_def::Function; +use crate::type_def::Generic; +use crate::type_def::Type; + +#[derive(Debug, agdb::TypeDefImpl)] +pub struct Impl { + pub name: &'static str, + pub generics: &'static [Generic], + pub trait_: Option Type>, + pub ty: fn() -> Type, + pub functions: &'static [Function], +} + +#[cfg(test)] +mod tests { + use crate::type_def::ImplDefinition; + use crate::type_def::Literal; + use crate::type_def::Type; + + #[derive(agdb::TypeDefImpl)] + #[allow(dead_code)] + struct ConstImplS; + + #[test] + fn empty_impl() { + #[derive(agdb::TypeDefImpl)] + struct S; + + let def = S::impl_def(); + + assert_eq!(def.name, "S"); + assert!(def.trait_.is_none()); + } + + #[test] + fn impl_for_trait() { + #[agdb::trait_def] + #[allow(dead_code)] + trait MyTrait {} + + #[derive(agdb::TypeDefImpl)] + struct S; + + let def = S::impl_def(); + + assert_eq!(def.name, "S"); + // Note: Trait binding detection requires impl block with #[agdb::impl_def] macro + // which conflicts with TypeDefImpl derive in tests + } + + #[test] + fn impl_with_function_self_ref() { + #[derive(agdb::TypeDefImpl)] + struct S; + + let def = S::impl_def(); + + assert_eq!(def.name, "S"); + // Note: Function details require impl block with #[agdb::impl_def] macro + // which conflicts with TypeDefImpl derive in tests + } + + #[test] + fn impl_with_function_self_mut_ref() { + #[derive(agdb::TypeDefImpl)] + struct S; + + let def = S::impl_def(); + + assert_eq!(def.name, "S"); + } + + #[test] + fn impl_with_function_self() { + #[derive(agdb::TypeDefImpl)] + struct S; + + let def = S::impl_def(); + + assert_eq!(def.name, "S"); + } + + #[test] + fn impl_with_function_self_mut() { + #[derive(agdb::TypeDefImpl)] + #[allow(dead_code)] + struct S { + i: i32, + } + + let def = S::impl_def(); + + assert_eq!(def.name, "S"); + } + + #[test] + fn impl_with_function_self_box() { + #[derive(agdb::TypeDefImpl)] + #[allow(dead_code)] + struct S { + i: i32, + } + + let def = S::impl_def(); + + assert_eq!(def.name, "S"); + } + + #[test] + fn impl_with_lifetime() { + #[derive(agdb::TypeDefImpl)] + #[allow(dead_code)] + struct S<'a> { + a: &'a str, + } + + let def = S::impl_def(); + + assert_eq!(def.name, "S"); + // Note: impl_def() captures impl block generics, not struct generics + // Struct generics are captured through type_def() method + } + + #[test] + fn impl_with_const_generic() { + let def = ConstImplS::<1>::impl_def(); + + assert_eq!(def.name, "ConstImplS"); + } + + #[test] + fn impl_function_with_nested_generic_containers() { + #[derive(agdb::TypeDef)] + struct S; + + #[agdb::impl_def] + #[allow(dead_code)] + impl S { + async fn transform( + &self, + input: Result<(u16, T), Option>>, + ) -> Option>> { + let _ = input; + None + } + } + + let def = S::impl_def(); + assert_eq!(def.functions.len(), 1); + + let f = &def.functions[0]; + assert_eq!(f.name, "transform"); + assert!(f.async_fn); + assert_eq!(f.generics.len(), 1); + assert_eq!(f.generics[0].name, "T"); + assert_eq!(f.generics[0].bounds.len(), 2); + + assert_eq!(f.args.len(), 2); + assert_eq!(f.args[0].name, "self"); + assert_eq!(f.args[1].name, "input"); + + let Type::Result { ok, err } = (f.args[1].ty.expect("expected type function"))() else { + panic!("Expected Result argument"); + }; + + let Type::Tuple(ok_fields) = ok() else { + panic!("Expected tuple in Result::Ok"); + }; + assert_eq!(ok_fields.len(), 2); + assert!(matches!((ok_fields[0])(), Type::Literal(Literal::U16))); + let Type::Generic(ok_t) = (ok_fields[1])() else { + panic!("Expected generic T in tuple"); + }; + assert_eq!(ok_t.name, "T"); + + let Type::Option(err_inner) = err() else { + panic!("Expected Option in Result::Err"); + }; + let Type::Vec(err_vec_inner) = err_inner() else { + panic!("Expected Vec in Option>"); + }; + let Type::Generic(err_t) = err_vec_inner() else { + panic!("Expected generic T in Vec"); + }; + assert_eq!(err_t.name, "T"); + + let Type::Option(ret_inner) = (f.ret)() else { + panic!("Expected Option return type"); + }; + let Type::Result { + ok: ret_ok, + err: ret_err, + } = ret_inner() + else { + panic!("Expected Result inside Option"); + }; + let Type::Generic(ret_ok_t) = ret_ok() else { + panic!("Expected generic T in Result::Ok"); + }; + assert_eq!(ret_ok_t.name, "T"); + let Type::Vec(ret_err_vec_inner) = ret_err() else { + panic!("Expected Vec in Result::Err"); + }; + let Type::Generic(ret_err_t) = ret_err_vec_inner() else { + panic!("Expected generic T in Vec"); + }; + assert_eq!(ret_err_t.name, "T"); + } +} diff --git a/agdb/src/type_def/struct_def.rs b/agdb/src/type_def/struct_def.rs new file mode 100644 index 00000000..2c80cbef --- /dev/null +++ b/agdb/src/type_def/struct_def.rs @@ -0,0 +1,268 @@ +use crate::type_def::Function; +use crate::type_def::Generic; +use crate::type_def::Variable; + +#[derive(Debug, agdb::TypeDefImpl)] +pub struct Struct { + pub name: &'static str, + pub generics: &'static [Generic], + pub fields: &'static [Variable], + pub functions: &'static [Function], +} + +#[cfg(test)] +mod tests { + use crate::type_def::GenericKind; + use crate::type_def::Literal; + use crate::type_def::Type; + use crate::type_def::TypeDefinition; + + #[test] + fn empty_struct() { + #[derive(agdb::TypeDefImpl)] + struct S {} + + let Type::Struct(s) = S::type_def() else { + panic!("Expected a struct type definition"); + }; + + assert_eq!(s.name, "S"); + } + + #[test] + fn empty_struct_no_braces() { + #[derive(agdb::TypeDefImpl)] + struct S; + + let Type::Struct(s) = S::type_def() else { + panic!("Expected a struct type definition"); + }; + + assert_eq!(s.name, "S"); + } + + #[test] + fn struct_with_fields() { + #[derive(agdb::TypeDefImpl)] + struct S { + _a: i32, + _b: String, + } + + let Type::Struct(s) = S::type_def() else { + panic!("Expected a struct type definition"); + }; + + assert_eq!(s.name, "S"); + assert_eq!(s.fields.len(), 2); + assert_eq!(s.fields[0].name, "_a"); + assert_eq!(s.fields[1].name, "_b"); + } + + #[test] + fn tuple_struct_with_fields() { + #[derive(agdb::TypeDefImpl)] + #[allow(dead_code)] + struct S(i32, String); + + let Type::Struct(s) = S::type_def() else { + panic!("Expected a struct type definition"); + }; + + assert_eq!(s.name, "S"); + assert_eq!(s.fields.len(), 2); + assert_eq!(s.fields[0].name, ""); + + let Type::Literal(Literal::I32) = (s.fields[0].ty.expect("expected type function"))() + else { + panic!("Expected a literal type definition"); + }; + + assert_eq!(s.fields[1].name, ""); + + let Type::Literal(Literal::String) = (s.fields[1].ty.expect("expected type function"))() + else { + panic!("Expected a literal type definition"); + }; + } + + #[test] + fn field_types() { + #[derive(agdb::TypeDefImpl)] + struct S { + _a: i32, + } + + let Type::Struct(s) = S::type_def() else { + panic!("Expected a struct type definition"); + }; + + let ty = (s.fields[0].ty.expect("expected type function"))(); + + let Type::Literal(Literal::I32) = ty else { + panic!("Expected a literal type definition"); + }; + } + + #[test] + fn generic_struct() { + #[derive(agdb::TypeDefImpl)] + struct S { + _a: T, + } + + let Type::Struct(s) = S::::type_def() else { + panic!("Expected a struct type definition"); + }; + + assert_eq!(s.name, "S"); + assert_eq!(s.fields.len(), 1); + assert_eq!(s.fields[0].name, "_a"); + + let ty = (s.fields[0].ty.expect("expected type function"))(); + + let Type::Generic(generic) = ty else { + panic!("Expected a generic type definition"); + }; + + assert_eq!(generic.name, "T"); + assert_eq!(generic.bounds.len(), 0); + } + + #[test] + fn generic_struct_with_bounds() { + #[agdb::trait_def] + trait MyTrait {} + + impl MyTrait for i32 {} + + #[derive(agdb::TypeDefImpl)] + struct S { + _a: T, + } + + let Type::Struct(s) = S::::type_def() else { + panic!("Expected a struct type definition"); + }; + + assert_eq!(s.name, "S"); + assert_eq!(s.fields.len(), 1); + assert_eq!(s.fields[0].name, "_a"); + + let ty = (s.fields[0].ty.expect("expected type function"))(); + + let Type::Generic(generic) = ty else { + panic!("Expected a generic type definition"); + }; + + assert_eq!(generic.name, "T"); + assert_eq!(generic.bounds.len(), 2); + let Type::Trait(bound_trait) = (generic.bounds[0])() else { + panic!("Expected a trait type definition"); + }; + + assert_eq!(bound_trait.name, "TypeDefinition"); + + let Type::Trait(bound_trait) = (generic.bounds[1])() else { + panic!("Expected a trait type definition"); + }; + + assert_eq!(bound_trait.name, "MyTrait"); + } + + #[test] + fn generic_struct_with_where_clause() { + #[derive(agdb::TypeDefImpl)] + struct S + where + T: agdb::type_def::TypeDefinition, + { + _a: T, + } + + let Type::Struct(s) = S::::type_def() else { + panic!("Expected a struct type definition"); + }; + + assert_eq!(s.name, "S"); + assert_eq!(s.fields.len(), 1); + assert_eq!(s.fields[0].name, "_a"); + + let ty = (s.fields[0].ty.expect("expected type function"))(); + + let Type::Generic(generic) = ty else { + panic!("Expected a generic type definition"); + }; + + assert_eq!(generic.name, "T"); + assert_eq!(generic.bounds.len(), 1); + let Type::Trait(bound_trait) = (generic.bounds[0])() else { + panic!("Expected a trait type definition"); + }; + + assert_eq!(bound_trait.name, "TypeDefinition"); + } + + #[test] + fn generic_tuple_struct() { + #[derive(agdb::TypeDefImpl)] + struct S(T); + + let Type::Struct(s) = S::::type_def() else { + panic!("Expected a struct type definition"); + }; + + assert_eq!(s.name, "S"); + assert_eq!(s.fields.len(), 1); + assert_eq!(s.fields[0].name, ""); + + let ty = (s.fields[0].ty.expect("expected type function"))(); + + let Type::Generic(generic) = ty else { + panic!("Expected a generic type definition"); + }; + + assert_eq!(generic.name, "T"); + assert_eq!(generic.bounds.len(), 0); + } + + #[test] + fn struct_with_lifetime() { + #[derive(agdb::TypeDefImpl)] + #[allow(dead_code)] + struct S<'a> { + _a: &'a str, + } + + let Type::Struct(s) = S::type_def() else { + panic!("Expected struct type definition"); + }; + + assert_eq!(s.generics.len(), 1); + assert!(matches!(s.generics[0].kind, GenericKind::Lifetime)); + assert_eq!(s.generics[0].name, "a"); + assert_eq!(s.generics[0].bounds.len(), 0); + assert_eq!(s.name, "S"); + } + + #[test] + fn struct_with_const_generic() { + #[derive(agdb::TypeDefImpl)] + #[allow(dead_code)] + struct S; + + let Type::Struct(s) = S::<1>::type_def() else { + panic!("Expected struct type definition"); + }; + + assert_eq!(s.generics.len(), 1); + assert!(matches!(s.generics[0].kind, GenericKind::Const)); + assert_eq!(s.generics[0].name, "N"); + assert_eq!(s.generics[0].bounds.len(), 1); + assert!( + matches!((s.generics[0].bounds[0])(), Type::Literal(Literal::Usize)), + "Got: {:?}", + (s.generics[0].bounds[0])() + ); + } +} diff --git a/agdb/src/type_def/trait_def.rs b/agdb/src/type_def/trait_def.rs new file mode 100644 index 00000000..96d3679a --- /dev/null +++ b/agdb/src/type_def/trait_def.rs @@ -0,0 +1,435 @@ +use crate::type_def::Function; +use crate::type_def::Generic; +use crate::type_def::Type; + +#[derive(Debug, agdb::TypeDefImpl)] +pub struct Trait { + pub name: &'static str, + pub generics: &'static [Generic], + pub bounds: &'static [fn() -> Type], + pub functions: &'static [Function], +} + +#[cfg(test)] +mod tests { + use crate::type_def::GenericKind; + use crate::type_def::Literal; + use crate::type_def::Type; + + #[test] + fn empty_trait() { + #[agdb::trait_def] + #[allow(dead_code)] + trait MyTrait {} + + let Type::Trait(def) = __MyTrait_type_def() else { + panic!("Expected a trait type definition"); + }; + + assert_eq!(def.name, "MyTrait"); + } + + #[test] + fn trait_with_generics() { + #[agdb::trait_def] + #[allow(dead_code)] + trait MyTrait {} + + let Type::Trait(def) = __MyTrait_type_def() else { + panic!("Expected a trait type definition"); + }; + + assert_eq!(def.generics.len(), 1); + let generic = &def.generics[0]; + assert_eq!(generic.name, "T"); + assert_eq!(generic.bounds.len(), 1); + let Type::Trait(bound_trait) = (generic.bounds[0])() else { + panic!("Expected a trait type definition"); + }; + assert_eq!(bound_trait.name, "TypeDefinition"); + } + + #[test] + fn trait_with_where_clause() { + #[agdb::trait_def] + #[allow(dead_code)] + trait MyTrait + where + T: agdb::type_def::TypeDefinition, + { + } + + let Type::Trait(def) = __MyTrait_type_def() else { + panic!("Expected a trait type definition"); + }; + + assert_eq!(def.generics.len(), 1); + let generic = &def.generics[0]; + assert_eq!(generic.name, "T"); + assert_eq!(generic.bounds.len(), 1); + let Type::Trait(bound_trait) = (generic.bounds[0])() else { + panic!("Expected a trait type definition"); + }; + assert_eq!(bound_trait.name, "TypeDefinition"); + } + + #[test] + fn trait_with_supertrait() { + #[agdb::trait_def] + #[allow(dead_code)] + trait MyTrait: agdb::type_def::TypeDefinition {} + + let Type::Trait(def) = __MyTrait_type_def() else { + panic!("Expected a trait type definition"); + }; + + assert_eq!(def.bounds.len(), 1); + let Type::Trait(bound_trait) = (def.bounds[0])() else { + panic!("Expected a trait type definition"); + }; + assert_eq!(bound_trait.name, "TypeDefinition"); + } + + #[test] + fn trait_with_functions() { + #[agdb::trait_def] + #[allow(dead_code)] + trait MyTrait { + fn a(); + async fn b(v: i32) -> String; + } + + let Type::Trait(def) = __MyTrait_type_def() else { + panic!("Expected a trait type definition"); + }; + + assert_eq!(def.functions.len(), 2); + + let a = &def.functions[0]; + assert_eq!(a.name, "a"); + assert_eq!(a.args.len(), 0); + assert!(!a.async_fn); + assert!( + matches!((a.ret)(), Type::Literal(Literal::Unit),), + "Got: {:?}", + (a.ret)() + ); + + let b = &def.functions[1]; + assert_eq!(b.name, "b"); + assert_eq!(b.args.len(), 1); + assert_eq!(b.args[0].name, "v"); + assert!(b.async_fn); + assert!( + matches!( + (b.args[0].ty.expect("expected type function"))(), + Type::Literal(Literal::I32), + ), + "Got: {:?}", + (b.args[0].ty.expect("expected type function"))() + ); + assert!( + matches!((b.ret)(), Type::Literal(Literal::String),), + "Got: {:?}", + (b.ret)() + ); + } + + #[test] + fn trait_function_with_generics() { + #[agdb::trait_def] + #[allow(dead_code)] + trait MyTrait { + fn id(v: T) -> T; + } + + let Type::Trait(def) = __MyTrait_type_def() else { + panic!("Expected a trait type definition"); + }; + + assert_eq!(def.functions.len(), 1); + let f = &def.functions[0]; + assert_eq!(f.name, "id"); + assert_eq!(f.generics.len(), 1); + assert_eq!(f.generics[0].name, "T"); + assert_eq!(f.generics[0].bounds.len(), 1); + + let Type::Trait(bound_trait) = (f.generics[0].bounds[0])() else { + panic!("Expected a trait type definition"); + }; + assert_eq!(bound_trait.name, "TypeDefinition"); + + let Type::Generic(arg_generic) = (f.args[0].ty.expect("expected type function"))() else { + panic!("Expected a generic type definition"); + }; + assert_eq!(arg_generic.name, "T"); + + let Type::Generic(ret_generic) = (f.ret)() else { + panic!("Expected a generic return type definition"); + }; + assert_eq!(ret_generic.name, "T"); + } + + #[test] + fn trait_with_lifetime() { + #[agdb::trait_def] + #[allow(dead_code)] + trait MyLifetimeTrait<'a> { + fn get(&'a self) -> &'a str; + } + + let Type::Trait(def) = __MyLifetimeTrait_type_def() else { + panic!("Expected trait type definition"); + }; + + assert_eq!(def.generics.len(), 1); + assert!(matches!(def.generics[0].kind, GenericKind::Lifetime)); + assert_eq!(def.generics[0].name, "a"); + assert_eq!(def.generics[0].bounds.len(), 0); + assert_eq!(def.functions.len(), 1); + assert_eq!(def.functions[0].generics.len(), 0); + assert_eq!(def.functions[0].args.len(), 1); + assert_eq!(def.functions[0].args[0].name, "self"); + assert!( + matches!( + (def.functions[0].args[0].ty.expect("expected type function"))(), + Type::Reference(crate::type_def::Reference { + mutable: false, + lifetime: Some("a"), + ty: _ + }), + ), + "Got: {:?}", + (def.functions[0].args[0].ty.expect("expected type function"))() + ); + } + + #[test] + fn trait_with_const_generic() { + #[agdb::trait_def] + #[allow(dead_code)] + trait MyConstTrait {} + + let Type::Trait(def) = __MyConstTrait_type_def() else { + panic!("Expected trait type definition"); + }; + + assert_eq!(def.generics.len(), 1); + assert!(matches!(def.generics[0].kind, GenericKind::Const)); + assert_eq!(def.generics[0].name, "N"); + assert_eq!(def.generics[0].bounds.len(), 1); + assert!( + matches!((def.generics[0].bounds[0])(), Type::Literal(Literal::Usize)), + "Got: {:?}", + (def.generics[0].bounds[0])() + ); + } + + #[test] + fn trait_function_with_default_implementation() { + #[agdb::trait_def] + #[allow(dead_code)] + trait MyTrait { + fn with_default() { + let _x = 42; + } + } + + let Type::Trait(def) = __MyTrait_type_def() else { + panic!("Expected a trait type definition"); + }; + + assert_eq!(def.functions.len(), 1); + let f = &def.functions[0]; + assert_eq!(f.name, "with_default"); + assert_eq!(f.args.len(), 0); + assert!(!f.async_fn); + assert!( + !f.body.is_empty(), + "Expected a body for default implementation" + ); + assert_eq!(f.body.len(), 1); + } + + #[test] + fn trait_function_without_default_implementation() { + #[agdb::trait_def] + #[allow(dead_code)] + trait MyTrait { + fn without_default(); + } + + let Type::Trait(def) = __MyTrait_type_def() else { + panic!("Expected a trait type definition"); + }; + + assert_eq!(def.functions.len(), 1); + let f = &def.functions[0]; + assert_eq!(f.name, "without_default"); + assert!( + f.body.is_empty(), + "Expected empty body for trait function without default implementation" + ); + } + + #[test] + fn trait_mixed_default_and_non_default_functions() { + #[agdb::trait_def] + #[allow(dead_code)] + trait MyTrait { + fn required(); + fn with_default() { + let _x = 42; + } + fn another_required(a: i32); + fn another_default(_b: String) -> bool { + true + } + } + + let Type::Trait(def) = __MyTrait_type_def() else { + panic!("Expected a trait type definition"); + }; + + assert_eq!(def.functions.len(), 4); + + let required = &def.functions[0]; + assert_eq!(required.name, "required"); + assert!(required.body.is_empty()); + + let with_default = &def.functions[1]; + assert_eq!(with_default.name, "with_default"); + assert!(!with_default.body.is_empty()); + + let another_required = &def.functions[2]; + assert_eq!(another_required.name, "another_required"); + assert!(another_required.body.is_empty()); + assert_eq!(another_required.args.len(), 1); + assert_eq!(another_required.args[0].name, "a"); + + let another_default = &def.functions[3]; + assert_eq!(another_default.name, "another_default"); + assert!(!another_default.body.is_empty()); + assert_eq!(another_default.args.len(), 1); + assert_eq!(another_default.args[0].name, "_b"); + } + + #[test] + fn trait_default_function_with_generics() { + #[agdb::trait_def] + #[allow(dead_code)] + trait MyTrait { + fn apply(val: T) -> T { + val + } + } + + let Type::Trait(def) = __MyTrait_type_def() else { + panic!("Expected a trait type definition"); + }; + + assert_eq!(def.functions.len(), 1); + let f = &def.functions[0]; + assert_eq!(f.name, "apply"); + assert_eq!(f.generics.len(), 1); + assert_eq!(f.generics[0].name, "T"); + assert_eq!(f.args.len(), 1); + assert_eq!(f.args[0].name, "val"); + assert!( + !f.body.is_empty(), + "Expected body for default generic function" + ); + } + + #[test] + fn trait_default_async_function() { + #[agdb::trait_def] + #[allow(dead_code)] + trait MyTrait { + async fn async_with_default() { + let _x = 1; + } + } + + let Type::Trait(def) = __MyTrait_type_def() else { + panic!("Expected a trait type definition"); + }; + + assert_eq!(def.functions.len(), 1); + let f = &def.functions[0]; + assert_eq!(f.name, "async_with_default"); + assert!(f.async_fn); + assert!(!f.body.is_empty()); + } + + #[test] + fn trait_function_with_nested_generic_containers() { + #[agdb::trait_def] + #[allow(dead_code)] + trait HttpLike { + async fn fetch( + &self, + input: Option, + ) -> Result<(u16, T), Option>>; + } + + let Type::Trait(def) = __HttpLike_type_def() else { + panic!("Expected a trait type definition"); + }; + + assert_eq!(def.functions.len(), 1); + let f = &def.functions[0]; + assert_eq!(f.name, "fetch"); + assert!(f.async_fn); + assert_eq!(f.generics.len(), 1); + assert_eq!(f.generics[0].name, "T"); + assert_eq!(f.generics[0].bounds.len(), 2); + + let Type::Trait(type_def_bound) = (f.generics[0].bounds[0])() else { + panic!("Expected TypeDefinition bound"); + }; + assert_eq!(type_def_bound.name, "TypeDefinition"); + + let Type::Trait(send_bound) = (f.generics[0].bounds[1])() else { + panic!("Expected Send bound"); + }; + assert_eq!(send_bound.name, "Send"); + + assert_eq!(f.args.len(), 2); + assert_eq!(f.args[0].name, "self"); + assert_eq!(f.args[1].name, "input"); + + let Type::Option(arg_inner) = (f.args[1].ty.expect("expected type function"))() else { + panic!("Expected Option argument"); + }; + let Type::Generic(arg_t) = arg_inner() else { + panic!("Expected generic inner type for Option"); + }; + assert_eq!(arg_t.name, "T"); + + let Type::Result { ok, err } = (f.ret)() else { + panic!("Expected Result return type"); + }; + + let Type::Tuple(ok_fields) = ok() else { + panic!("Expected tuple in Result::Ok"); + }; + assert_eq!(ok_fields.len(), 2); + assert!(matches!((ok_fields[0])(), Type::Literal(Literal::U16))); + let Type::Generic(ok_t) = (ok_fields[1])() else { + panic!("Expected generic T in tuple"); + }; + assert_eq!(ok_t.name, "T"); + + let Type::Option(err_inner) = err() else { + panic!("Expected Option in Result::Err"); + }; + let Type::Vec(vec_inner) = err_inner() else { + panic!("Expected Vec in Option>"); + }; + let Type::Generic(err_t) = vec_inner() else { + panic!("Expected generic T in Vec"); + }; + assert_eq!(err_t.name, "T"); + } +} diff --git a/agdb/tests/api_def_reflection_test.rs b/agdb/tests/api_def_reflection_test.rs new file mode 100644 index 00000000..396fff7b --- /dev/null +++ b/agdb/tests/api_def_reflection_test.rs @@ -0,0 +1,2177 @@ +#![cfg(feature = "api")] + +use agdb::type_def::Expression; +use agdb::type_def::GenericKind; +use agdb::type_def::ImplDefinition; +use agdb::type_def::Literal; +use agdb::type_def::LiteralValue; +use agdb::type_def::Op; +use agdb::type_def::PointerKind; +use agdb::type_def::Type; +use agdb::type_def::TypeDefinition; + +fn named_type(ty: &agdb::type_def::Variable) -> Type { + (ty.ty.expect("expected type function"))() +} + +fn get_body(name: &str) -> &'static [Expression] { + let func_type = match name { + "literal_integer" => __literal_integer_type_def(), + "literal_bool" => __literal_bool_type_def(), + "literal_string" => __literal_string_type_def(), + "literal_float" => __literal_float_type_def(), + "literal_suffixed" => __literal_suffixed_type_def(), + "array_expr" => __array_expr_type_def(), + "assign_expr" => __assign_expr_type_def(), + "binary_arithmetic" => __binary_arithmetic_type_def(), + "binary_comparison" => __binary_comparison_type_def(), + "binary_logical" => __binary_logical_type_def(), + "binary_assign_ops" => __binary_assign_ops_type_def(), + "unary_neg" => __unary_neg_type_def(), + "unary_not" => __unary_not_type_def(), + "block_expr" => __block_expr_type_def(), + "break_continue" => __break_continue_type_def(), + "call_function" => __call_function_type_def(), + "call_method" => __call_method_type_def(), + "closure_simple" => __closure_simple_type_def(), + "closure_typed" => __closure_typed_type_def(), + "field_access_expr" => __field_access_expr_type_def(), + "tuple_access_expr" => __tuple_access_expr_type_def(), + "for_loop_expr" => __for_loop_expr_type_def(), + "while_loop_expr" => __while_loop_expr_type_def(), + "loop_expr" => __loop_expr_type_def(), + "if_expr" => __if_expr_type_def(), + "if_else_expr" => __if_else_expr_type_def(), + "if_else_if_expr" => __if_else_if_expr_type_def(), + "index_expr" => __index_expr_type_def(), + "let_simple" => __let_simple_type_def(), + "let_typed" => __let_typed_type_def(), + "let_no_init" => __let_no_init_type_def(), + "reference_expr" => __reference_expr_type_def(), + "return_value" => __return_value_type_def(), + "return_none" => __return_none_type_def(), + "try_expr" => __try_expr_type_def(), + "tuple_expr" => __tuple_expr_type_def(), + "format_expr" => __format_expr_type_def(), + "vec_macro_expr" => __vec_macro_expr_type_def(), + "match_expr" => __match_expr_type_def(), + "struct_expr" => __struct_expr_type_def(), + "implicit_return" => __implicit_return_type_def(), + "ident_expr" => __ident_expr_type_def(), + "wild_expr" => __wild_expr_type_def(), + "path_expr" => __path_expr_type_def(), + "let_pattern_tuple" => __let_pattern_tuple_type_def(), + "for_pattern" => __for_pattern_type_def(), + _ => panic!("Unknown test function: {name}"), + }; + + let Type::Function(def) = func_type else { + panic!("Expected function type definition"); + }; + + def.body +} + +#[test] +fn test_type_def_literals() { + assert!(matches!(bool::type_def(), Type::Literal(Literal::Bool))); + assert!(matches!(i32::type_def(), Type::Literal(Literal::I32))); + assert!(matches!(f64::type_def(), Type::Literal(Literal::F64))); + assert!(matches!(String::type_def(), Type::Literal(Literal::String))); +} + +#[test] +fn options() { + let Type::Option(inner) = Option::::type_def() else { + panic!("Expected an option type definition"); + }; + + assert!(matches!(inner(), Type::Literal(Literal::I32))); +} + +#[test] +fn results() { + let Type::Result { ok, err } = Result::::type_def() else { + panic!("Expected a result type definition"); + }; + + assert!(matches!(ok(), Type::Literal(Literal::I32))); + assert!(matches!(err(), Type::Literal(Literal::String))); +} + +#[test] +fn derive_type_enum_itself() { + let Type::Enum(def) = Type::type_def() else { + panic!("Expected enum type definition for Type"); + }; + + assert_eq!(def.name, "Type"); + assert!(def.variants.iter().any(|v| v.name == "Function")); + assert!(def.variants.iter().any(|v| v.name == "Trait")); +} + +#[test] +fn function_pointer_type_def() { + let Type::Function(def) = String as TypeDefinition>::type_def() else { + panic!("Expected function type definition"); + }; + + assert_eq!(def.args.len(), 1); + assert_eq!(def.args[0].name, ""); + assert!(matches!( + named_type(&def.args[0]), + Type::Literal(Literal::I32) + )); + assert!(matches!((def.ret)(), Type::Literal(Literal::String))); +} + +#[test] +fn empty_struct() { + #[derive(agdb::TypeDefImpl)] + struct S {} + + let Type::Struct(s) = S::type_def() else { + panic!("Expected a struct type definition"); + }; + + assert_eq!(s.name, "S"); +} + +#[test] +fn empty_struct_no_braces() { + #[derive(agdb::TypeDefImpl)] + struct S; + + let Type::Struct(s) = S::type_def() else { + panic!("Expected a struct type definition"); + }; + + assert_eq!(s.name, "S"); +} + +#[test] +fn struct_with_fields() { + #[derive(agdb::TypeDefImpl)] + struct S { + _a: i32, + _b: String, + } + + let Type::Struct(s) = S::type_def() else { + panic!("Expected a struct type definition"); + }; + + assert_eq!(s.fields.len(), 2); + assert_eq!(s.fields[0].name, "_a"); + assert_eq!(s.fields[1].name, "_b"); +} + +#[test] +fn tuple_struct_with_fields() { + #[derive(agdb::TypeDefImpl)] + #[expect(dead_code)] + struct S(i32, String); + + let Type::Struct(s) = S::type_def() else { + panic!("Expected a struct type definition"); + }; + + assert_eq!(s.fields.len(), 2); + assert_eq!(s.fields[0].name, ""); + assert!(matches!( + named_type(&s.fields[0]), + Type::Literal(Literal::I32) + )); + assert_eq!(s.fields[1].name, ""); + assert!(matches!( + named_type(&s.fields[1]), + Type::Literal(Literal::String) + )); +} + +#[test] +fn field_types() { + #[derive(agdb::TypeDefImpl)] + struct S { + _a: i32, + } + + let Type::Struct(s) = S::type_def() else { + panic!("Expected a struct type definition"); + }; + + assert!(matches!( + named_type(&s.fields[0]), + Type::Literal(Literal::I32) + )); +} + +#[test] +fn generic_struct() { + #[derive(agdb::TypeDefImpl)] + struct S { + _a: T, + } + + let Type::Struct(s) = S::::type_def() else { + panic!("Expected a struct type definition"); + }; + + let Type::Generic(generic) = named_type(&s.fields[0]) else { + panic!("Expected a generic type definition"); + }; + + assert_eq!(generic.name, "T"); + assert_eq!(generic.bounds.len(), 0); +} + +#[test] +fn generic_struct_with_bounds() { + #[agdb::trait_def] + trait MyTrait {} + + impl MyTrait for i32 {} + + #[derive(agdb::TypeDefImpl)] + struct S { + _a: T, + } + + let Type::Struct(s) = S::::type_def() else { + panic!("Expected a struct type definition"); + }; + + let Type::Generic(generic) = named_type(&s.fields[0]) else { + panic!("Expected a generic type definition"); + }; + + assert_eq!(generic.name, "T"); + assert_eq!(generic.bounds.len(), 2); + assert!(matches!((generic.bounds[0])(), Type::Trait(_))); + assert!(matches!((generic.bounds[1])(), Type::Trait(_))); +} + +#[test] +fn generic_struct_with_where_clause() { + #[derive(agdb::TypeDefImpl)] + struct S + where + T: agdb::type_def::TypeDefinition, + { + _a: T, + } + + let Type::Struct(s) = S::::type_def() else { + panic!("Expected a struct type definition"); + }; + + let Type::Generic(generic) = named_type(&s.fields[0]) else { + panic!("Expected a generic type definition"); + }; + + assert_eq!(generic.name, "T"); + assert_eq!(generic.bounds.len(), 1); + assert!(matches!((generic.bounds[0])(), Type::Trait(_))); +} + +#[test] +fn generic_tuple_struct() { + #[derive(agdb::TypeDefImpl)] + struct S(T); + + let Type::Struct(s) = S::::type_def() else { + panic!("Expected a struct type definition"); + }; + + let Type::Generic(generic) = named_type(&s.fields[0]) else { + panic!("Expected a generic type definition"); + }; + + assert_eq!(generic.name, "T"); + assert_eq!(generic.bounds.len(), 0); +} + +#[test] +fn struct_with_lifetime() { + #[derive(agdb::TypeDefImpl)] + struct S<'a> { + _a: &'a str, + } + + let Type::Struct(s) = S::type_def() else { + panic!("Expected struct type definition"); + }; + + assert_eq!(s.generics.len(), 1); + assert!(matches!(s.generics[0].kind, GenericKind::Lifetime)); + assert_eq!(s.generics[0].name, "a"); +} + +#[test] +fn struct_with_const_generic() { + #[derive(agdb::TypeDefImpl)] + struct S; + + let Type::Struct(s) = S::<1>::type_def() else { + panic!("Expected struct type definition"); + }; + + assert_eq!(s.generics.len(), 1); + assert!(matches!(s.generics[0].kind, GenericKind::Const)); + assert_eq!(s.generics[0].name, "N"); + assert_eq!(s.generics[0].bounds.len(), 1); + assert!(matches!( + (s.generics[0].bounds[0])(), + Type::Literal(Literal::Usize) + )); +} + +#[test] +fn empty_enum() { + #[derive(agdb::TypeDefImpl)] + enum E {} + + let Type::Enum(e) = E::type_def() else { + panic!("Expected an enum type definition"); + }; + + assert_eq!(e.name, "E"); +} + +#[test] +fn enum_with_variants() { + #[derive(agdb::TypeDefImpl)] + #[expect(dead_code)] + enum E { + A, + B, + } + + let Type::Enum(e) = E::type_def() else { + panic!("Expected an enum type definition"); + }; + + assert_eq!(e.variants.len(), 2); + assert_eq!(e.variants[0].name, "A"); + assert!(matches!( + named_type(&e.variants[0]), + Type::Literal(Literal::Unit) + )); + assert_eq!(e.variants[1].name, "B"); + assert!(matches!( + named_type(&e.variants[1]), + Type::Literal(Literal::Unit) + )); +} + +#[test] +fn enum_with_typed_variant() { + #[derive(agdb::TypeDefImpl)] + #[expect(dead_code)] + enum E { + A(String), + } + + let Type::Enum(e) = E::type_def() else { + panic!("Expected an enum type definition"); + }; + + assert!(matches!( + named_type(&e.variants[0]), + Type::Literal(Literal::String) + )); +} + +#[test] +fn enum_with_struct_variant() { + #[derive(agdb::TypeDefImpl)] + #[expect(dead_code)] + enum E { + A { _a: String }, + } + + let Type::Enum(e) = E::type_def() else { + panic!("Expected an enum type definition"); + }; + + let Type::Struct(s) = named_type(&e.variants[0]) else { + panic!("Expected a struct type definition"); + }; + assert_eq!(s.fields.len(), 1); + assert_eq!(s.fields[0].name, "_a"); + assert!(matches!( + named_type(&s.fields[0]), + Type::Literal(Literal::String) + )); +} + +#[test] +fn enum_with_tuple_variant() { + #[derive(agdb::TypeDefImpl)] + #[expect(dead_code)] + enum E { + A(String, i32), + } + + let Type::Enum(e) = E::type_def() else { + panic!("Expected an enum type definition"); + }; + + let Type::Tuple(fields) = named_type(&e.variants[0]) else { + panic!("Expected a tuple type definition"); + }; + assert_eq!(fields.len(), 2); + assert!(matches!((fields[0])(), Type::Literal(Literal::String))); + assert!(matches!((fields[1])(), Type::Literal(Literal::I32))); +} + +#[test] +fn generic_enum() { + #[derive(agdb::TypeDefImpl)] + #[expect(dead_code)] + enum E { + A(T), + } + + let Type::Enum(e) = E::::type_def() else { + panic!("Expected an enum type definition"); + }; + + let Type::Generic(generic) = named_type(&e.variants[0]) else { + panic!("Expected a generic type definition"); + }; + + assert_eq!(generic.name, "T"); + assert_eq!(generic.bounds.len(), 0); +} + +#[test] +fn generic_enum_with_bounds() { + #[agdb::trait_def] + trait MyTrait {} + + impl MyTrait for i32 {} + + #[derive(agdb::TypeDefImpl)] + #[expect(dead_code)] + enum E { + A(T), + } + + let Type::Enum(e) = E::::type_def() else { + panic!("Expected an enum type definition"); + }; + + assert_eq!(e.generics[0].bounds.len(), 2); + let Type::Generic(generic) = named_type(&e.variants[0]) else { + panic!("Expected a generic type definition"); + }; + assert_eq!(generic.bounds.len(), 2); +} + +#[test] +fn generic_enum_with_where_clause() { + #[derive(agdb::TypeDefImpl)] + #[expect(dead_code)] + enum E + where + T: agdb::type_def::TypeDefinition, + { + A(T), + } + + let Type::Enum(e) = E::::type_def() else { + panic!("Expected an enum type definition"); + }; + + let Type::Generic(generic) = named_type(&e.variants[0]) else { + panic!("Expected a generic type definition"); + }; + assert_eq!(generic.bounds.len(), 1); +} + +#[test] +fn generic_enum_with_struct_variant() { + #[derive(agdb::TypeDefImpl)] + #[expect(dead_code)] + enum E { + A { _a: T }, + } + + let Type::Enum(e) = E::::type_def() else { + panic!("Expected an enum type definition"); + }; + + let Type::Struct(s) = named_type(&e.variants[0]) else { + panic!("Expected a struct type definition"); + }; + let Type::Generic(generic) = named_type(&s.fields[0]) else { + panic!("Expected a generic type definition"); + }; + assert_eq!(generic.name, "T"); +} + +#[test] +fn generic_enum_with_tuple_variant_multiple_types() { + #[derive(agdb::TypeDefImpl)] + #[expect(dead_code)] + enum E { + A(T, i32), + } + + let Type::Enum(e) = E::::type_def() else { + panic!("Expected an enum type definition"); + }; + + let Type::Tuple(fields) = named_type(&e.variants[0]) else { + panic!("Expected a tuple type definition"); + }; + let Type::Generic(generic) = (fields[0])() else { + panic!("Expected a generic type definition"); + }; + assert_eq!(generic.name, "T"); + assert!(matches!((fields[1])(), Type::Literal(Literal::I32))); +} + +#[test] +fn enum_with_lifetime() { + #[derive(agdb::TypeDefImpl)] + #[expect(dead_code)] + enum E<'a> { + A(&'a str), + } + + let Type::Enum(e) = E::type_def() else { + panic!("Expected enum type definition"); + }; + + assert!(matches!(e.generics[0].kind, GenericKind::Lifetime)); + assert_eq!(e.generics[0].name, "a"); +} + +#[test] +fn enum_with_const_generic() { + #[derive(agdb::TypeDefImpl)] + #[expect(dead_code)] + enum E { + A, + } + + let Type::Enum(e) = E::<1>::type_def() else { + panic!("Expected enum type definition"); + }; + + assert!(matches!(e.generics[0].kind, GenericKind::Const)); + assert_eq!(e.generics[0].name, "N"); + assert!(matches!( + (e.generics[0].bounds[0])(), + Type::Literal(Literal::Usize) + )); +} + +#[test] +fn empty_function() { + #[agdb::fn_def] + #[expect(dead_code)] + fn my_function() {} + + let Type::Function(def) = __my_function_type_def() else { + panic!("Expected a function type definition"); + }; + + assert_eq!(def.name, "my_function"); + assert_eq!(def.args.len(), 0); + assert!(matches!((def.ret)(), Type::Literal(Literal::Unit))); +} + +#[test] +fn function_with_arguments() { + #[agdb::fn_def] + #[allow(dead_code, unused_variables)] + fn my_function(a: i32, b: String) {} + + let Type::Function(def) = __my_function_type_def() else { + panic!("Expected a function type definition"); + }; + + assert_eq!(def.args.len(), 2); + assert_eq!(def.args[0].name, "a"); + assert_eq!(def.args[1].name, "b"); + assert!(matches!( + named_type(&def.args[0]), + Type::Literal(Literal::I32) + )); + assert!(matches!( + named_type(&def.args[1]), + Type::Literal(Literal::String) + )); +} + +#[test] +fn generic_function_argument_and_return() { + #[agdb::fn_def] + #[allow(dead_code, unused_variables)] + fn my_function(value: T) -> T { + panic!("body should not be used by fn_def parser") + } + + let Type::Function(def) = __my_function_type_def() else { + panic!("Expected a function type definition"); + }; + + assert_eq!(def.generics.len(), 1); + assert_eq!(def.generics[0].name, "T"); + assert_eq!(def.generics[0].bounds.len(), 1); + let Type::Generic(arg_generic) = named_type(&def.args[0]) else { + panic!("Expected a generic type definition"); + }; + assert_eq!(arg_generic.name, "T"); + let Type::Generic(ret_generic) = (def.ret)() else { + panic!("Expected a generic return type definition"); + }; + assert_eq!(ret_generic.name, "T"); +} + +#[test] +fn function_with_lifetime() { + #[agdb::fn_def] + #[allow(dead_code, clippy::needless_lifetimes)] + fn borrow<'a>(s: &'a str) -> &'a str { + s + } + + let Type::Function(def) = __borrow_type_def() else { + panic!("Expected function type definition"); + }; + + assert!(matches!(def.generics[0].kind, GenericKind::Lifetime)); + assert_eq!(def.generics[0].name, "a"); + assert!(matches!(named_type(&def.args[0]), Type::Reference(_))); +} + +#[test] +fn function_with_args_with_lifetime() { + #[agdb::fn_def] + #[allow(dead_code, clippy::needless_lifetimes)] + fn borrow<'a>(s: &'a mut Vec) -> &'a Vec { + s + } + + let Type::Function(def) = __borrow_type_def() else { + panic!("Expected function type definition"); + }; + + assert!(matches!(def.generics[0].kind, GenericKind::Lifetime)); + let Type::Reference(reference) = named_type(&def.args[0]) else { + panic!("Expected reference type definition"); + }; + assert!(reference.mutable); + assert_eq!(reference.lifetime, Some("a")); +} + +#[test] +fn function_with_const_generic() { + #[agdb::fn_def] + #[expect(dead_code)] + fn with_const() {} + + let Type::Function(def) = __with_const_type_def() else { + panic!("Expected function type definition"); + }; + + assert!(matches!(def.generics[0].kind, GenericKind::Const)); + assert_eq!(def.generics[0].name, "N"); + assert!(matches!( + (def.generics[0].bounds[0])(), + Type::Literal(Literal::Usize) + )); +} + +#[test] +fn empty_impl() { + #[derive(agdb::TypeDef)] + struct S; + + #[agdb::impl_def] + impl S {} + + let def = S::impl_def(); + + assert_eq!(def.name, "S"); + assert!(def.trait_.is_none()); +} + +#[test] +fn impl_for_trait() { + #[agdb::trait_def] + #[allow(dead_code)] + trait MyTrait {} + + #[derive(agdb::TypeDef)] + struct S; + + #[agdb::impl_def] + impl MyTrait for S {} + + let def = S::impl_def(); + let Some(tr) = def.trait_ else { + panic!("Expected a trait bound"); + }; + let Type::Trait(trait_def) = tr() else { + panic!("Expected a trait type definition"); + }; + assert_eq!(trait_def.name, "MyTrait"); +} + +#[test] +fn impl_with_function_self_ref() { + #[derive(agdb::TypeDef)] + struct S; + + #[agdb::impl_def] + #[expect(dead_code)] + impl S { + fn foo(&self) -> i32 { + 42 + } + } + + let def = S::impl_def(); + assert_eq!(def.functions[0].name, "foo"); + assert!(matches!( + named_type(&def.functions[0].args[0]), + Type::Reference(_) + )); +} + +#[test] +fn impl_with_function_self_mut_ref() { + #[derive(agdb::TypeDef)] + struct S; + + #[agdb::impl_def] + #[expect(dead_code)] + impl S { + fn foo(&mut self) -> i32 { + 42 + } + } + + let def = S::impl_def(); + let Type::Reference(reference) = named_type(&def.functions[0].args[0]) else { + panic!("Expected reference type definition"); + }; + assert!(reference.mutable); +} + +#[test] +fn impl_with_function_self() { + #[derive(agdb::TypeDef)] + struct S; + + #[agdb::impl_def] + #[expect(dead_code)] + impl S { + fn foo(self) -> i32 { + 42 + } + } + + let def = S::impl_def(); + assert!(matches!( + named_type(&def.functions[0].args[0]), + Type::SelfType(false) + )); +} + +#[test] +fn impl_with_function_self_mut() { + #[derive(agdb::TypeDef)] + struct S { + i: i32, + } + + #[agdb::impl_def] + #[expect(dead_code)] + impl S { + fn foo(mut self) -> i32 { + self.i = 42; + self.i + } + } + + let def = S::impl_def(); + assert!(matches!( + named_type(&def.functions[0].args[0]), + Type::SelfType(true) + )); +} + +#[test] +fn impl_with_function_self_box() { + #[derive(agdb::TypeDef)] + #[expect(dead_code)] + struct S { + i: i32, + } + + #[agdb::impl_def] + #[allow(clippy::boxed_local, dead_code)] + impl S { + fn foo(self: Box) -> i32 { + 42 + } + } + + let def = S::impl_def(); + let Type::Pointer(pointer) = named_type(&def.functions[0].args[0]) else { + panic!("Expected pointer type definition"); + }; + assert!(matches!(pointer.kind, PointerKind::Box)); +} + +#[test] +fn impl_with_lifetime() { + #[derive(agdb::TypeDef)] + struct S<'a> { + a: &'a str, + } + + #[agdb::impl_def] + #[expect(dead_code)] + impl<'a> S<'a> { + fn get(&'a self) -> &'a str { + self.a + } + } + + let def = S::impl_def(); + assert!(matches!(def.generics[0].kind, GenericKind::Lifetime)); + let Type::Reference(reference) = named_type(&def.functions[0].args[0]) else { + panic!("Expected reference type definition"); + }; + assert_eq!(reference.lifetime, Some("a")); + assert!(!reference.mutable); +} + +#[test] +fn impl_with_const_generic() { + #[derive(agdb::TypeDef)] + struct ConstImplS; + + #[agdb::impl_def] + impl ConstImplS {} + + let def = ConstImplS::<1>::impl_def(); + assert!(matches!(def.generics[0].kind, GenericKind::Const)); + assert_eq!(def.generics[0].name, "N"); +} + +#[test] +fn empty_trait() { + #[agdb::trait_def] + #[expect(dead_code)] + trait MyTrait {} + + let Type::Trait(def) = __MyTrait_type_def() else { + panic!("Expected a trait type definition"); + }; + + assert_eq!(def.name, "MyTrait"); +} + +#[test] +fn trait_with_generics() { + #[agdb::trait_def] + #[expect(dead_code)] + trait MyTrait {} + + let Type::Trait(def) = __MyTrait_type_def() else { + panic!("Expected a trait type definition"); + }; + + assert_eq!(def.generics.len(), 1); + assert_eq!(def.generics[0].name, "T"); + assert_eq!(def.generics[0].bounds.len(), 1); +} + +#[test] +fn trait_with_where_clause() { + #[agdb::trait_def] + #[expect(dead_code)] + trait MyTrait + where + T: agdb::type_def::TypeDefinition, + { + } + + let Type::Trait(def) = __MyTrait_type_def() else { + panic!("Expected a trait type definition"); + }; + + assert_eq!(def.generics.len(), 1); + assert_eq!(def.generics[0].name, "T"); +} + +#[test] +fn trait_with_supertrait() { + #[agdb::trait_def] + #[expect(dead_code)] + trait MyTrait: agdb::type_def::TypeDefinition {} + + let Type::Trait(def) = __MyTrait_type_def() else { + panic!("Expected a trait type definition"); + }; + + assert_eq!(def.bounds.len(), 1); +} + +#[test] +fn trait_with_functions() { + #[agdb::trait_def] + #[expect(dead_code)] + trait MyTrait { + fn a(); + async fn b(v: i32) -> String; + } + + let Type::Trait(def) = __MyTrait_type_def() else { + panic!("Expected a trait type definition"); + }; + + assert_eq!(def.functions.len(), 2); + assert_eq!(def.functions[0].name, "a"); + assert!(!def.functions[0].async_fn); + assert_eq!(def.functions[1].name, "b"); + assert!(def.functions[1].async_fn); + assert!(matches!( + named_type(&def.functions[1].args[0]), + Type::Literal(Literal::I32) + )); +} + +#[test] +fn trait_function_with_generics() { + #[agdb::trait_def] + #[expect(dead_code)] + trait MyTrait { + fn id(v: T) -> T; + } + + let Type::Trait(def) = __MyTrait_type_def() else { + panic!("Expected a trait type definition"); + }; + + let f = &def.functions[0]; + assert_eq!(f.name, "id"); + assert_eq!(f.generics.len(), 1); + let Type::Generic(arg_generic) = named_type(&f.args[0]) else { + panic!("Expected a generic type definition"); + }; + assert_eq!(arg_generic.name, "T"); +} + +#[test] +fn trait_with_lifetime() { + #[agdb::trait_def] + #[expect(dead_code)] + trait MyLifetimeTrait<'a> { + fn get(&'a self) -> &'a str; + } + + let Type::Trait(def) = __MyLifetimeTrait_type_def() else { + panic!("Expected trait type definition"); + }; + + assert!(matches!(def.generics[0].kind, GenericKind::Lifetime)); + let Type::Reference(reference) = named_type(&def.functions[0].args[0]) else { + panic!("Expected reference type definition"); + }; + assert_eq!(reference.lifetime, Some("a")); +} + +#[test] +fn trait_with_const_generic() { + #[agdb::trait_def] + #[expect(dead_code)] + trait MyConstTrait {} + + let Type::Trait(def) = __MyConstTrait_type_def() else { + panic!("Expected trait type definition"); + }; + + assert!(matches!(def.generics[0].kind, GenericKind::Const)); + assert_eq!(def.generics[0].name, "N"); +} + +#[test] +fn trait_function_with_default_implementation() { + #[agdb::trait_def] + #[expect(dead_code)] + trait MyTrait { + fn with_default() { + let _x = 42; + } + } + + let Type::Trait(def) = __MyTrait_type_def() else { + panic!("Expected a trait type definition"); + }; + + let f = &def.functions[0]; + assert_eq!(f.name, "with_default"); + assert!(!f.body.is_empty()); +} + +#[test] +fn trait_function_without_default_implementation() { + #[agdb::trait_def] + #[expect(dead_code)] + trait MyTrait { + fn without_default(); + } + + let Type::Trait(def) = __MyTrait_type_def() else { + panic!("Expected a trait type definition"); + }; + + assert!(def.functions[0].body.is_empty()); +} + +#[test] +fn trait_mixed_default_and_non_default_functions() { + #[agdb::trait_def] + #[expect(dead_code)] + trait MyTrait { + fn required(); + fn with_default() { + let _x = 42; + } + fn another_required(a: i32); + fn another_default(_b: String) -> bool { + true + } + } + + let Type::Trait(def) = __MyTrait_type_def() else { + panic!("Expected a trait type definition"); + }; + + assert_eq!(def.functions.len(), 4); + assert!(def.functions[0].body.is_empty()); + assert!(!def.functions[1].body.is_empty()); + assert!(def.functions[2].body.is_empty()); + assert!(!def.functions[3].body.is_empty()); +} + +#[test] +fn trait_default_function_with_generics() { + #[agdb::trait_def] + #[expect(dead_code)] + trait MyTrait { + fn apply(val: T) -> T { + val + } + } + + let Type::Trait(def) = __MyTrait_type_def() else { + panic!("Expected a trait type definition"); + }; + + assert_eq!(def.functions[0].generics.len(), 1); + assert!(!def.functions[0].body.is_empty()); +} + +#[test] +fn trait_default_async_function() { + #[agdb::trait_def] + #[expect(dead_code)] + trait MyTrait { + async fn async_with_default() { + let _x = 1; + } + } + + let Type::Trait(def) = __MyTrait_type_def() else { + panic!("Expected a trait type definition"); + }; + + assert!(def.functions[0].async_fn); + assert!(!def.functions[0].body.is_empty()); +} + +#[agdb::fn_def] +#[allow(unused)] +fn literal_integer() -> i32 { + 42 +} + +#[test] +fn test_literal_integer() { + let body = get_body("literal_integer"); + assert_eq!(body.len(), 1); + assert!(matches!( + &body[0], + Expression::Return(Some(Expression::Literal(LiteralValue::I32(42)))) + )); +} + +#[agdb::fn_def] +#[allow(unused)] +fn literal_bool() -> bool { + true +} + +#[test] +fn test_literal_bool() { + let body = get_body("literal_bool"); + assert_eq!(body.len(), 1); + assert!(matches!( + &body[0], + Expression::Return(Some(Expression::Literal(LiteralValue::Bool(true)))) + )); +} + +#[agdb::fn_def] +#[allow(unused)] +fn literal_string() -> &'static str { + "hello" +} + +#[test] +fn test_literal_string() { + let body = get_body("literal_string"); + match &body[0] { + Expression::Return(Some(Expression::Literal(LiteralValue::Str(s)))) => { + assert_eq!(*s, "hello"); + } + _ => panic!("Got: {:?}", body[0]), + } +} + +#[agdb::fn_def] +#[allow(unused)] +fn literal_float() -> f64 { + 3.3 +} + +#[test] +fn test_literal_float() { + let body = get_body("literal_float"); + match &body[0] { + Expression::Return(Some(Expression::Literal(LiteralValue::F64(v)))) => { + assert!((*v - 3.3).abs() < f64::EPSILON); + } + _ => panic!("Got: {:?}", body[0]), + } +} + +#[agdb::fn_def] +#[allow(unused)] +fn literal_suffixed() { + let _a = 1u8; + let _b = 2u16; + let _c = 3u32; + let _d = 4u64; + let _e = 5i8; + let _f = 6i16; + let _g = 7i32; + let _h = 8usize; + let _i = 1.0f32; +} + +#[test] +fn test_literal_suffixed() { + let body = get_body("literal_suffixed"); + assert_eq!(body.len(), 9); + assert!(matches!( + &body[0], + Expression::Let { + value: Some(Expression::Literal(LiteralValue::U8(1))), + .. + } + )); + assert!(matches!( + &body[1], + Expression::Let { + value: Some(Expression::Literal(LiteralValue::U16(2))), + .. + } + )); + assert!(matches!( + &body[2], + Expression::Let { + value: Some(Expression::Literal(LiteralValue::U32(3))), + .. + } + )); + assert!(matches!( + &body[3], + Expression::Let { + value: Some(Expression::Literal(LiteralValue::U64(4))), + .. + } + )); + assert!(matches!( + &body[4], + Expression::Let { + value: Some(Expression::Literal(LiteralValue::I8(5))), + .. + } + )); + assert!(matches!( + &body[5], + Expression::Let { + value: Some(Expression::Literal(LiteralValue::I16(6))), + .. + } + )); + assert!(matches!( + &body[6], + Expression::Let { + value: Some(Expression::Literal(LiteralValue::I32(7))), + .. + } + )); + assert!(matches!( + &body[7], + Expression::Let { + value: Some(Expression::Literal(LiteralValue::Usize(8))), + .. + } + )); + assert!(matches!( + &body[8], + Expression::Let { + value: Some(Expression::Literal(LiteralValue::F32(_))), + .. + } + )); +} + +#[agdb::fn_def] +#[allow(unused)] +fn array_expr() { + let _arr = [1, 2, 3]; +} + +#[test] +fn test_array() { + let body = get_body("array_expr"); + match &body[0] { + Expression::Let { + value: Some(Expression::Array(elems)), + .. + } => assert_eq!(elems.len(), 3), + _ => panic!("Got: {:?}", body[0]), + } +} + +#[agdb::fn_def] +#[allow(unused)] +fn assign_expr() { + let mut x = 1; + x = 2; +} + +#[test] +fn test_assign() { + let body = get_body("assign_expr"); + match &body[1] { + Expression::Assign { target, value } => { + assert!(matches!(target, Expression::Ident("x"))); + assert!(matches!(value, Expression::Literal(LiteralValue::I32(2)))); + } + _ => panic!("Got: {:?}", body[1]), + } +} + +#[agdb::fn_def] +#[allow(unused)] +fn binary_arithmetic() { + let _a = 1 + 2; + let _b = 3 - 4; + let _c = 5 * 6; + let _d = 7 / 8; + let _e = 19 % 10; +} + +#[test] +fn test_binary_arithmetic() { + let body = get_body("binary_arithmetic"); + assert!(matches!( + &body[0], + Expression::Let { + value: Some(Expression::Binary { op: Op::Add, .. }), + .. + } + )); + assert!(matches!( + &body[1], + Expression::Let { + value: Some(Expression::Binary { op: Op::Sub, .. }), + .. + } + )); + assert!(matches!( + &body[2], + Expression::Let { + value: Some(Expression::Binary { op: Op::Mul, .. }), + .. + } + )); + assert!(matches!( + &body[3], + Expression::Let { + value: Some(Expression::Binary { op: Op::Div, .. }), + .. + } + )); + assert!(matches!( + &body[4], + Expression::Let { + value: Some(Expression::Binary { op: Op::Rem, .. }), + .. + } + )); +} + +#[agdb::fn_def] +#[allow(unused)] +fn binary_comparison() { + let _a = 1 == 2; + let _b = 1 != 2; + let _c = 1 < 2; + let _d = 1 <= 2; + let _e = 1 > 2; + let _f = 1 >= 2; +} + +#[test] +fn test_binary_comparison() { + let body = get_body("binary_comparison"); + let ops = [Op::Eq, Op::Ne, Op::Lt, Op::Le, Op::Gt, Op::Ge]; + for (i, expected_op) in ops.iter().enumerate() { + match &body[i] { + Expression::Let { + value: Some(Expression::Binary { op, .. }), + .. + } => { + assert!(std::mem::discriminant(op) == std::mem::discriminant(expected_op)); + } + _ => panic!("body[{i}]: {:?}", body[i]), + } + } +} + +#[agdb::fn_def] +#[allow(unused, clippy::nonminimal_bool)] +fn binary_logical() { + let _a = true && false; + let _b = true || false; +} + +#[test] +fn test_binary_logical() { + let body = get_body("binary_logical"); + assert!(matches!( + &body[0], + Expression::Let { + value: Some(Expression::Binary { op: Op::And, .. }), + .. + } + )); + assert!(matches!( + &body[1], + Expression::Let { + value: Some(Expression::Binary { op: Op::Or, .. }), + .. + } + )); +} + +#[agdb::fn_def] +#[allow(unused)] +fn binary_assign_ops() { + let mut x = 0; + x += 1; + x -= 1; + x *= 2; + x /= 2; + x %= 3; +} + +#[test] +fn test_binary_assign_ops() { + let body = get_body("binary_assign_ops"); + let ops = [ + Op::AddAssign, + Op::SubAssign, + Op::MulAssign, + Op::DivAssign, + Op::RemAssign, + ]; + for (i, expected_op) in ops.iter().enumerate() { + match &body[i + 1] { + Expression::Binary { op, .. } => { + assert!(std::mem::discriminant(op) == std::mem::discriminant(expected_op)); + } + _ => panic!("body[{}]: {:?}", i + 1, body[i + 1]), + } + } +} + +#[agdb::fn_def] +#[allow(unused)] +fn unary_neg() -> i32 { + -5 +} + +#[test] +fn test_unary_neg() { + let body = get_body("unary_neg"); + assert!(matches!( + &body[0], + Expression::Return(Some(Expression::Unary { op: Op::Neg, .. })) + )); +} + +#[agdb::fn_def] +#[allow(unused, clippy::nonminimal_bool)] +fn unary_not() -> bool { + !true +} + +#[test] +fn test_unary_not() { + let body = get_body("unary_not"); + assert!(matches!( + &body[0], + Expression::Return(Some(Expression::Unary { op: Op::Not, .. })) + )); +} + +#[agdb::fn_def] +#[allow(unused)] +fn block_expr() { + { + let _x = 1; + }; +} + +#[test] +fn test_block() { + let body = get_body("block_expr"); + match &body[0] { + Expression::Block(stmts) => { + assert_eq!(stmts.len(), 1); + assert!(matches!(stmts[0], Expression::Let { .. })); + } + _ => panic!("Got: {:?}", body[0]), + } +} + +#[agdb::fn_def] +#[allow(unused, clippy::never_loop)] +fn break_continue() { + loop { + break; + } + loop { + continue; + } +} + +#[test] +fn test_break_continue() { + let body = get_body("break_continue"); + match &body[0] { + Expression::While { body, .. } => match body { + Expression::Block(stmts) => assert!(matches!(stmts[0], Expression::Break)), + _ => panic!("Expected block, got {:?}", body), + }, + _ => panic!("Got: {:?}", body[0]), + } + match &body[1] { + Expression::While { body, .. } => match body { + Expression::Block(stmts) => assert!(matches!(stmts[0], Expression::Continue)), + _ => panic!("Expected block, got {:?}", body), + }, + _ => panic!("Got: {:?}", body[1]), + } +} + +#[agdb::fn_def] +#[allow(unused)] +fn call_function() { + fn helper(_x: i32) -> i32 { + 0 + } + let _r = helper(42); +} + +#[test] +fn test_call_function() { + let body = get_body("call_function"); + match &body[1] { + Expression::Let { + value: + Some(Expression::Call { + recipient: None, + function, + args, + }), + .. + } => { + assert!(matches!(function, Expression::Ident("helper"))); + assert_eq!(args.len(), 1); + } + _ => panic!("Got: {:?}", body[1]), + } +} + +#[agdb::fn_def] +#[allow(unused)] +fn call_method() { + let v = [1, 2, 3]; + let _len = v.len(); +} + +#[test] +fn test_call_method() { + let body = get_body("call_method"); + match &body[1] { + Expression::Let { + value: + Some(Expression::Call { + recipient: Some(_), + function, + args, + }), + .. + } => { + assert!(matches!(function, Expression::Path { ident: "len", .. })); + assert_eq!(args.len(), 0); + } + _ => panic!("Got: {:?}", body[1]), + } +} + +#[agdb::fn_def] +#[allow(unused)] +fn closure_simple() { + let _f = |x: i32| x; +} + +#[test] +fn test_closure_simple() { + let body = get_body("closure_simple"); + match &body[0] { + Expression::Let { + value: Some(Expression::Closure(func)), + .. + } => { + assert_eq!(func.name, ""); + assert_eq!(func.args.len(), 1); + assert_eq!(func.args[0].name, "x"); + } + _ => panic!("Got: {:?}", body[0]), + } +} + +#[agdb::fn_def] +#[allow(unused)] +fn closure_typed() { + let _f = |x: i32| -> i32 { x + 1 }; +} + +#[test] +fn test_closure_typed() { + let body = get_body("closure_typed"); + match &body[0] { + Expression::Let { + value: Some(Expression::Closure(func)), + .. + } => { + assert_eq!(func.args.len(), 1); + assert_eq!(func.args[0].name, "x"); + assert!(!func.body.is_empty()); + } + _ => panic!("Got: {:?}", body[0]), + } +} + +#[agdb::fn_def] +#[allow(unused)] +fn field_access_expr() { + struct S { + field: i32, + } + let s = S { field: 1 }; + let _f = s.field; +} + +#[test] +fn test_field_access() { + let body = get_body("field_access_expr"); + match &body[2] { + Expression::Let { + value: + Some(Expression::FieldAccess { + base, + field: "field", + }), + .. + } => { + assert!(matches!(base, Expression::Ident("s"))); + } + _ => panic!("Got: {:?}", body[2]), + } +} + +#[agdb::fn_def] +#[allow(unused)] +fn tuple_access_expr() { + let t = (1, 2); + let _first = t.0; +} + +#[test] +fn test_tuple_access() { + let body = get_body("tuple_access_expr"); + match &body[1] { + Expression::Let { + value: Some(Expression::TupleAccess { base, index: 0 }), + .. + } => { + assert!(matches!(base, Expression::Ident("t"))); + } + _ => panic!("Got: {:?}", body[1]), + } +} + +#[agdb::fn_def] +#[allow(unused)] +fn for_loop_expr() { + let items = [1, 2, 3]; + for _item in items { + let _x = 1; + } +} + +#[test] +fn test_for_loop() { + let body = get_body("for_loop_expr"); + match &body[1] { + Expression::For { + pattern, + iterable, + body, + } => { + assert!(matches!(pattern, Expression::Ident("_item"))); + assert!(matches!(iterable, Expression::Ident("items"))); + assert!(matches!(body, Expression::Block(_))); + } + _ => panic!("Got: {:?}", body[1]), + } +} + +#[agdb::fn_def] +#[allow(unused)] +fn while_loop_expr() { + let mut i = 0; + while i < 10 { + i += 1; + } +} + +#[test] +fn test_while_loop() { + let body = get_body("while_loop_expr"); + match &body[1] { + Expression::While { condition, body } => { + assert!(matches!(condition, Expression::Binary { op: Op::Lt, .. })); + assert!(matches!(body, Expression::Block(_))); + } + _ => panic!("Got: {:?}", body[1]), + } +} + +#[agdb::fn_def] +#[allow(unused, clippy::never_loop)] +fn loop_expr() { + loop { + break; + } +} + +#[test] +fn test_loop_desugars_to_while_true() { + let body = get_body("loop_expr"); + match &body[0] { + Expression::While { condition, .. } => { + assert!(matches!( + condition, + Expression::Literal(LiteralValue::Bool(true)) + )); + } + _ => panic!("Got: {:?}", body[0]), + } +} + +#[agdb::fn_def] +#[allow(unused)] +fn if_expr() { + if true { + let _x = 1; + } +} + +#[test] +fn test_if() { + let body = get_body("if_expr"); + match &body[0] { + Expression::If { + condition, + then_branch, + else_branch: None, + } => { + assert!(matches!( + condition, + Expression::Literal(LiteralValue::Bool(true)) + )); + assert!(matches!(then_branch, Expression::Block(_))); + } + _ => panic!("Got: {:?}", body[0]), + } +} + +#[agdb::fn_def] +#[allow(unused)] +fn if_else_expr() { + if true { + let _x = 1; + } else { + let _y = 2; + } +} + +#[test] +fn test_if_else() { + let body = get_body("if_else_expr"); + match &body[0] { + Expression::If { + else_branch: Some(eb), + .. + } => { + assert!(matches!(eb, Expression::Block(_))); + } + _ => panic!("Got: {:?}", body[0]), + } +} + +#[agdb::fn_def] +#[allow(unused)] +fn if_else_if_expr() { + if true { + let _x = 1; + } else if false { + let _y = 2; + } else { + let _z = 3; + } +} + +#[test] +fn test_if_else_if() { + let body = get_body("if_else_if_expr"); + match &body[0] { + Expression::If { + else_branch: + Some(Expression::If { + else_branch: Some(_), + .. + }), + .. + } => {} + _ => panic!("Got: {:?}", body[0]), + } +} + +#[agdb::fn_def] +#[allow(unused)] +fn index_expr() { + let arr = [1, 2, 3]; + let _v = arr[0]; +} + +#[test] +fn test_index() { + let body = get_body("index_expr"); + match &body[1] { + Expression::Let { + value: Some(Expression::Index { base, index }), + .. + } => { + assert!(matches!(base, Expression::Ident("arr"))); + assert!(matches!(index, Expression::Literal(LiteralValue::I32(0)))); + } + _ => panic!("Got: {:?}", body[1]), + } +} + +#[agdb::fn_def] +#[allow(unused)] +fn let_simple() { + let _x = 42; +} + +#[test] +fn test_let_simple() { + let body = get_body("let_simple"); + match &body[0] { + Expression::Let { + name, + ty: None, + value: Some(Expression::Literal(LiteralValue::I32(42))), + } => { + assert!(matches!(name, Expression::Ident("_x"))); + } + _ => panic!("Got: {:?}", body[0]), + } +} + +#[agdb::fn_def] +#[allow(unused)] +fn let_typed() { + let _x: i32 = 42; +} + +#[test] +fn test_let_typed() { + let body = get_body("let_typed"); + match &body[0] { + Expression::Let { + name, + ty: Some(ty_fn), + value: Some(_), + } => { + assert!(matches!(name, Expression::Ident("_x"))); + assert!(matches!(ty_fn(), Type::Literal(Literal::I32))); + } + _ => panic!("Got: {:?}", body[0]), + } +} + +#[agdb::fn_def] +#[allow(unused)] +fn let_no_init() { + let _x: i32; +} + +#[test] +fn test_let_no_init() { + let body = get_body("let_no_init"); + assert!(matches!( + &body[0], + Expression::Let { + value: None, + ty: Some(_), + .. + } + )); +} + +#[agdb::fn_def] +#[allow(unused)] +fn reference_expr() { + let x = 42; + let _r = &x; +} + +#[test] +fn test_reference() { + let body = get_body("reference_expr"); + match &body[1] { + Expression::Let { + value: Some(Expression::Reference(inner)), + .. + } => { + assert!(matches!(inner, Expression::Ident("x"))); + } + _ => panic!("Got: {:?}", body[1]), + } +} + +#[agdb::fn_def] +#[allow(unused, clippy::needless_return)] +fn return_value() -> i32 { + return 42; +} + +#[test] +fn test_return_value() { + let body = get_body("return_value"); + assert!(matches!( + &body[0], + Expression::Return(Some(Expression::Literal(LiteralValue::I32(42)))) + )); +} + +#[agdb::fn_def] +#[allow(unused, clippy::needless_return)] +fn return_none() { + return; +} + +#[test] +fn test_return_none() { + let body = get_body("return_none"); + assert!(matches!(&body[0], Expression::Return(None))); +} + +#[agdb::fn_def] +#[allow(unused)] +fn try_expr() -> Result { + let r: Result = Ok(1); + r?; + Ok(0) +} + +#[test] +fn test_try() { + let body = get_body("try_expr"); + match &body[1] { + Expression::Try(inner) => assert!(matches!(inner, Expression::Ident("r"))), + _ => panic!("Got: {:?}", body[1]), + } +} + +#[agdb::fn_def] +#[allow(unused)] +fn tuple_expr() { + let _t = (1, 2, 3); +} + +#[test] +fn test_tuple() { + let body = get_body("tuple_expr"); + match &body[0] { + Expression::Let { + value: Some(Expression::Tuple(elems)), + .. + } => assert_eq!(elems.len(), 3), + _ => panic!("Got: {:?}", body[0]), + } +} + +#[agdb::fn_def] +#[allow(unused)] +fn format_expr() { + let x = 42; + let _s = format!("{}", x); +} + +#[test] +fn test_format_macro() { + let body = get_body("format_expr"); + match &body[1] { + Expression::Let { + value: + Some(Expression::Format { + format_string, + args, + }), + .. + } => { + assert_eq!(*format_string, "{}"); + assert_eq!(args.len(), 1); + assert!(matches!(args[0], Expression::Ident("x"))); + } + _ => panic!("Got: {:?}", body[1]), + } +} + +#[agdb::fn_def] +#[allow(unused, clippy::useless_vec)] +fn vec_macro_expr() { + let _v = vec![1, 2, 3]; +} + +#[test] +fn test_vec_macro() { + let body = get_body("vec_macro_expr"); + match &body[0] { + Expression::Let { + value: Some(Expression::Array(elems)), + .. + } => assert_eq!(elems.len(), 3), + _ => panic!("Got: {:?}", body[0]), + } +} + +#[agdb::fn_def] +#[allow(unused)] +fn match_expr() -> i32 { + let x = 1; + match x { + 1 => 10, + 2 => 20, + _ => 0, + } +} + +#[test] +fn test_match() { + let body = get_body("match_expr"); + match &body[1] { + Expression::If { + condition, + then_branch, + else_branch: Some(_), + } => { + assert!(matches!(condition, Expression::Binary { op: Op::Eq, .. })); + assert!(matches!(then_branch, Expression::Block(_))); + } + _ => panic!("Got: {:?}", body[1]), + } +} + +#[agdb::fn_def] +#[allow(unused)] +fn struct_expr() { + struct Point { + x: i32, + y: i32, + } + let _p = Point { x: 1, y: 2 }; +} + +#[test] +fn test_struct_expr() { + let body = get_body("struct_expr"); + match &body[1] { + Expression::Let { + value: Some(Expression::Struct { name, fields }), + .. + } => { + assert!(matches!(name, Expression::Ident("Point"))); + assert_eq!(fields.len(), 2); + assert_eq!(fields[0].0, "x"); + assert_eq!(fields[1].0, "y"); + } + _ => panic!("Got: {:?}", body[1]), + } +} + +#[agdb::fn_def] +#[allow(unused, clippy::let_and_return)] +fn implicit_return() -> i32 { + let x = 5; + x +} + +#[test] +fn test_implicit_return() { + let body = get_body("implicit_return"); + assert!(matches!( + &body[1], + Expression::Return(Some(Expression::Ident("x"))) + )); +} + +#[agdb::fn_def] +#[allow(unused)] +fn ident_expr() { + let x = 1; + let _y = x; +} + +#[test] +fn test_ident() { + let body = get_body("ident_expr"); + assert!(matches!( + &body[1], + Expression::Let { + value: Some(Expression::Ident("x")), + .. + } + )); +} + +#[agdb::fn_def] +#[allow(unused)] +fn wild_expr() { + let _ = 42; +} + +#[test] +fn test_wild() { + let body = get_body("wild_expr"); + assert!(matches!( + &body[0], + Expression::Let { + name: Expression::Wild, + .. + } + )); +} + +#[agdb::fn_def] +#[allow(unused)] +fn path_expr() { + let _v: Option = None; +} + +#[test] +fn test_path() { + let body = get_body("path_expr"); + assert!(matches!( + &body[0], + Expression::Let { + value: Some(Expression::Ident("None")), + .. + } + )); +} + +#[agdb::fn_def] +#[allow(unused)] +fn let_pattern_tuple() { + let (a, b) = (1, 2); + let _ = a + b; +} + +#[test] +fn test_let_pattern_tuple() { + let body = get_body("let_pattern_tuple"); + match &body[0] { + Expression::Let { + name: Expression::Tuple(elems), + .. + } => { + assert_eq!(elems.len(), 2); + assert!(matches!(elems[0], Expression::Ident("a"))); + assert!(matches!(elems[1], Expression::Ident("b"))); + } + _ => panic!("Got: {:?}", body[0]), + } +} + +#[agdb::fn_def] +#[allow(unused)] +fn for_pattern() { + let items = [(1, 2), (3, 4)]; + for (a, _b) in items { + let _ = a; + } +} + +#[test] +fn test_for_pattern() { + let body = get_body("for_pattern"); + match &body[1] { + Expression::For { pattern, .. } => assert!(matches!(pattern, Expression::Tuple(_))), + _ => panic!("Got: {:?}", body[1]), + } +} diff --git a/agdb_api/rust/src/client.rs b/agdb_api/rust/src/client.rs index 046cfb9c..f0378559 100644 --- a/agdb_api/rust/src/client.rs +++ b/agdb_api/rust/src/client.rs @@ -17,7 +17,8 @@ use agdb::QueryResult; use agdb::QueryType; #[cfg(feature = "api")] -pub trait AgdbApiClient: HttpClient + agdb::api_def::TypeDefinition {} +#[cfg_attr(feature = "api", agdb::trait_def())] +pub trait AgdbApiClient: HttpClient + agdb::type_def::TypeDefinition {} #[cfg(not(feature = "api"))] pub trait AgdbApiClient: HttpClient {} diff --git a/agdb_api/rust/src/http_client.rs b/agdb_api/rust/src/http_client.rs index 89ead55d..76fcfd58 100644 --- a/agdb_api/rust/src/http_client.rs +++ b/agdb_api/rust/src/http_client.rs @@ -2,85 +2,87 @@ use crate::AgdbApiError; use crate::api_result::AgdbApiResult; use crate::client::AgdbApiClient; #[cfg(feature = "api")] -use agdb::api_def::{ImplDefinition, Type, TypeDefinition}; +use agdb::type_def::{ImplDefinition, Type, TypeDefinition}; use serde::Serialize; use serde::de::DeserializeOwned; +#[cfg_attr(feature = "api", agdb::trait_def())] +#[allow(async_fn_in_trait)] pub trait HttpClient { - fn delete( + async fn delete(&self, uri: &str, token: &Option) -> AgdbApiResult; + async fn get( &self, uri: &str, token: &Option, - ) -> impl std::future::Future> + Send; - fn get( - &self, - uri: &str, - token: &Option, - ) -> impl std::future::Future> + Send; - fn post( + ) -> AgdbApiResult<(u16, T)>; + async fn post( &self, uri: &str, json: Option, token: &Option, - ) -> impl std::future::Future> + Send; - fn put( + ) -> AgdbApiResult<(u16, R)>; + async fn put( &self, uri: &str, json: Option, token: &Option, - ) -> impl std::future::Future> + Send; + ) -> AgdbApiResult; } -pub struct ReqwestClient { - pub client: reqwest::Client, +pub struct ReqwestClientTypeDef(pub reqwest::Client); + +impl std::ops::Deref for ReqwestClientTypeDef { + type Target = reqwest::Client; + + fn deref(&self) -> &Self::Target { + &self.0 + } } -impl AgdbApiClient for ReqwestClient {} +impl std::ops::DerefMut for ReqwestClientTypeDef { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} #[cfg(feature = "api")] -impl TypeDefinition for ReqwestClient { +impl TypeDefinition for ReqwestClientTypeDef { fn type_def() -> Type { - use agdb::api_def::NamedType; - - Type::Struct(agdb::api_def::struct_def::Struct { - name: "ReqwestClient", + Type::Struct(agdb::type_def::Struct { + name: "reqwest::Client", generics: &[], - fields: &[NamedType { - name: "client", - ty: Some(|| { - Type::Struct(agdb::api_def::struct_def::Struct { - name: "HttpClient", - generics: &[], - fields: &[], - functions: &[], - }) - }), - }], + fields: &[], functions: &[], }) } } #[cfg(feature = "api")] -impl ImplDefinition for ReqwestClient { - fn functions() -> &'static [agdb::api_def::Function] { - &[] - } +impl ImplDefinition for ReqwestClientTypeDef {} + +#[cfg_attr(feature = "api", derive(agdb::TypeDef))] +pub struct ReqwestClient { + pub client: ReqwestClientTypeDef, } +impl AgdbApiClient for ReqwestClient {} + impl ReqwestClient { #[allow(clippy::new_without_default)] pub fn new() -> Self { Self { - client: reqwest::Client::new(), + client: ReqwestClientTypeDef(reqwest::Client::new()), } } pub fn with_client(client: reqwest::Client) -> Self { - Self { client } + Self { + client: ReqwestClientTypeDef(client), + } } } +#[cfg_attr(feature = "api", agdb::impl_def())] impl HttpClient for ReqwestClient { async fn delete(&self, uri: &str, token: &Option) -> AgdbApiResult { let mut request = self.client.delete(uri); diff --git a/agdb_derive/src/api_def.rs b/agdb_derive/src/api_def.rs index 925def7d..3ef2fc41 100644 --- a/agdb_derive/src/api_def.rs +++ b/agdb_derive/src/api_def.rs @@ -1,30 +1,33 @@ -pub(crate) mod enum_def; -pub(crate) mod expression; -pub(crate) mod function_def; -pub(crate) mod generics; -pub(crate) mod statement; -pub(crate) mod struct_def; -pub(crate) mod tuple_def; -pub(crate) mod type_def; +pub(crate) mod enum_parser; +pub(crate) mod expression_parser; +pub(crate) mod function_parser; +pub(crate) mod generics_parser; +pub(crate) mod impl_parser; +pub(crate) mod struct_parser; +pub(crate) mod trait_parser; use proc_macro::TokenStream; use proc_macro2::TokenStream as TokenStream2; use quote::quote; use syn::DeriveInput; -use syn::ImplItem; -use syn::ItemImpl; -use syn::parse_macro_input; +use syn::Ident; pub(crate) fn type_def_impl(item: TokenStream) -> TokenStream { - let input = parse_macro_input!(item as DeriveInput); - type_def::type_def(input).into() + let input = syn::parse_macro_input!(item as DeriveInput); + + match &input.data { + syn::Data::Struct(s) => struct_parser::parse_struct(&input, s), + syn::Data::Enum(e) => enum_parser::parse_enum(&input, e), + _ => unimplemented!("Only structs and enums are supported for now"), + } + .into() } pub(crate) fn type_def_impl_impl(item: TokenStream) -> TokenStream { let it = item.clone(); let def: TokenStream2 = type_def_impl(item).into(); - let input = parse_macro_input!(it as DeriveInput); + let input = syn::parse_macro_input!(it as DeriveInput); let name = input.ident; let (impl_generics, ty_generic, where_clause) = input.generics.split_for_impl(); @@ -38,32 +41,57 @@ pub(crate) fn type_def_impl_impl(item: TokenStream) -> TokenStream { pub(crate) fn impl_def_impl(item: TokenStream) -> TokenStream { let it: TokenStream2 = item.clone().into(); - let impl_block = parse_macro_input!(item as ItemImpl); - let ty = impl_block.self_ty; - let funcs = impl_block - .items - .iter() - .filter_map(|i| { - if let ImplItem::Fn(f) = i { - Some(function_def::parse_function(f, &impl_block.generics)) - } else { - None - } - }) - .collect::>(); - let funcs_len = funcs.len(); - - let (impl_generics, _ty_generic, where_clause) = impl_block.generics.split_for_impl(); + let def_impl = if let Ok(input) = syn::parse::(item) { + impl_parser::parse_impl(&input) + } else { + unimplemented!("Only impl blocks are supported") + }; + + quote! { + #it + + #def_impl + } + .into() +} + +pub(crate) fn trait_def_impl(item: TokenStream) -> TokenStream { + let it: TokenStream2 = item.clone().into(); + let def_fn = if let Ok(input) = syn::parse::(item) { + trait_parser::parse_trait(&input) + } else { + unimplemented!("Only traits are supported") + }; quote! { #it - impl #impl_generics ::agdb::api_def::ImplDefinition for #ty #where_clause { - fn functions() -> &'static [::agdb::api_def::Function] { - const FUNCTIONS: [::agdb::api_def::Function; #funcs_len] = [#(#funcs),*]; - &FUNCTIONS - } - } + #def_fn } .into() } + +pub(crate) fn fn_def_impl(item: TokenStream) -> TokenStream { + let it: TokenStream2 = item.clone().into(); + let def_fn = if let Ok(input) = syn::parse::(item) { + function_parser::parse_function(&input) + } else { + unimplemented!("Only functions are supported") + }; + + quote! { + #it + + #def_fn + } + .into() +} + +pub(crate) fn type_def_fn(name: &String) -> TokenStream2 { + let bound_fn_name = Ident::new( + &format!("__{name}_type_def"), + proc_macro2::Span::call_site(), + ); + + quote! { #bound_fn_name } +} diff --git a/agdb_derive/src/api_def/enum_def.rs b/agdb_derive/src/api_def/enum_def.rs deleted file mode 100644 index 6343d1ec..00000000 --- a/agdb_derive/src/api_def/enum_def.rs +++ /dev/null @@ -1,86 +0,0 @@ -use super::generics; -use super::struct_def; -use super::tuple_def; -use proc_macro2::TokenStream; -use quote::quote; -use syn::DataEnum; -use syn::DeriveInput; -use syn::Fields; -use syn::Ident; -use syn::Token; -use syn::Variant; -use syn::punctuated::Punctuated; - -pub(crate) fn parse_enum(e: &DataEnum, input: &DeriveInput) -> TokenStream { - let name = &input.ident; - let generic_names = generics::list_generics(&input.generics); - let generics = generics::parse_generics(name, &input.generics); - let variants = parse_variants(name, &e.variants, &generic_names); - let (impl_generics, ty_generic, where_clause) = input.generics.split_for_impl(); - - quote! { - impl #impl_generics ::agdb::api_def::TypeDefinition for #name #ty_generic #where_clause { - fn type_def() -> ::agdb::api_def::Type { - ::agdb::api_def::Type::Enum(::agdb::api_def::Enum { - name: stringify!(#name), - generics: &[#(#generics),*], - variants: &[#(#variants),*], - functions: <#name #ty_generic as ::agdb::api_def::ImplDefinition>::functions(), - }) - } - } - } -} - -fn parse_variants( - name: &Ident, - variants: &Punctuated, - generics: &[String], -) -> Vec { - variants - .iter() - .map(|v| { - let variant_name = &v.ident; - match &v.fields { - Fields::Named(fields_named) => { - let fields = struct_def::parse_named_fields(name, Some(fields_named), generics); - - quote! { - ::agdb::api_def::NamedType { - name: stringify!(#variant_name), - ty: Some(|| ::agdb::api_def::Type::Struct(::agdb::api_def::Struct { - name: stringify!(#variant_name), - generics: &[], - fields: &[#(#fields),*], - functions: &[], - })), - } - } - } - Fields::Unnamed(fields_unnamed) => { - let fields = tuple_def::parse_unnamed_fields(Some(fields_unnamed), generics); - - quote! { - ::agdb::api_def::NamedType { - name: stringify!(#variant_name), - ty: Some(|| ::agdb::api_def::Type::Tuple(::agdb::api_def::Tuple { - name: stringify!(#variant_name), - generics: &[], - fields: &[#(#fields),*], - functions: &[], - })), - } - } - } - Fields::Unit => { - quote! { - ::agdb::api_def::NamedType { - name: stringify!(#variant_name), - ty: None, - } - } - } - } - }) - .collect() -} diff --git a/agdb_derive/src/api_def/expression.rs b/agdb_derive/src/api_def/expression.rs deleted file mode 100644 index 99782f79..00000000 --- a/agdb_derive/src/api_def/expression.rs +++ /dev/null @@ -1,144 +0,0 @@ -mod array; -mod block; -mod call; -mod closure; -mod conditionals; -mod literal; -mod loops; -mod macros; -mod object; -mod op; -mod path; -pub(crate) mod pattern; - -use super::statement::ExpressionContext; -use proc_macro2::TokenStream; -use quote::quote; -use syn::Expr; -use syn::ExprReference; -use syn::ExprTry; - -pub(crate) fn parse_expression(e: &Expr, context: ExpressionContext) -> TokenStream { - let parsed = parse_expression_impl(e, context); - - if context.last && !context.semicolon && is_returnable(e) { - quote! { - ::agdb::api_def::Expression::Return(Some(&#parsed)) - } - } else { - parsed - } -} - -fn parse_expression_impl(e: &Expr, context: ExpressionContext) -> TokenStream { - match e { - Expr::Array(e) => array::parse_array(e, context), - Expr::Assign(e) => op::parse_assign(e, context), - Expr::Async(e) => block::parse_block(&e.block, context), - Expr::Await(e) => parse_await(&e.base, context), - Expr::Binary(e) => op::parse_binary_op(e, context), - Expr::Block(e) => block::parse_block(&e.block, context), - Expr::Break(e) => loops::parse_break(e, context), - Expr::Call(e) => call::parse_call(e, context), - Expr::Cast(e) => parse_expression(&e.expr, context), - Expr::Closure(e) => closure::parse_closure(e, context), - Expr::Const(e) => block::parse_block(&e.block, context), - Expr::Continue(e) => loops::parse_continue(e, context), - Expr::Field(e) => object::parse_field_access(e, context), - Expr::ForLoop(e) => loops::parse_for_loop(e, context), - Expr::Group(_) => quote! {}, // group is invisible and ignored - Expr::If(e) => conditionals::parse_if(e, context), - Expr::Index(e) => array::parse_index(e, context), - Expr::Infer(_) => parse_infer(), - Expr::Let(e) => pattern::parse_let(&e.pat, Some(&e.expr), context), - Expr::Lit(e) => literal::parse_literal(&e.lit, context), - Expr::Loop(e) => loops::parse_loop(e, context), - Expr::Macro(e) => macros::parse_macros(e, context), - Expr::Match(e) => conditionals::parse_match(e, context), - Expr::MethodCall(e) => call::parse_method_call(e, context), - Expr::Paren(e) => parse_expression_impl(&e.expr, context), - Expr::Path(e) => path::parse_path(&e.path, context), - Expr::Range(e) => panic!( - "{}: range expressions are not supported: {e:?}", - context.fn_name - ), - Expr::RawAddr(e) => panic!("{}: raw address are not supported: {e:?}", context.fn_name), - Expr::Reference(e) => parse_reference(e, context), - Expr::Repeat(e) => panic!( - "{}: repeat expressions are not supported: {e:?}", - context.fn_name - ), - Expr::Return(e) => parse_return(&e.expr, context), - Expr::Struct(e) => object::parse_struct(e, context), - Expr::Try(e) => parse_try(e, context), - Expr::TryBlock(e) => block::parse_block(&e.block, context), - Expr::Tuple(e) => object::parse_tuple(e, context), - Expr::Unary(e) => op::parse_unary_op(e, context), - Expr::Unsafe(e) => panic!( - "{}: unsafe expressions are not supported: {e:?}", - context.fn_name - ), - Expr::Verbatim(e) => panic!( - "{}: verbatim expressions are not supported: {e:?}", - context.fn_name - ), - Expr::While(e) => loops::parse_while_loop(e, context), - Expr::Yield(e) => panic!( - "{}: yield expressions are not supported: {e:?}", - context.fn_name - ), - _ => panic!("Unsupported expression: {}", context.fn_name), - } -} - -fn parse_await(expr: &Expr, context: ExpressionContext) -> TokenStream { - let expr = parse_expression(expr, context); - quote! { - ::agdb::api_def::Expression::Await(&#expr) - } -} - -fn parse_infer() -> TokenStream { - quote! { - ::agdb::api_def::Expression::Wild - } -} - -fn parse_reference(reference: &ExprReference, context: ExpressionContext) -> TokenStream { - let expr = parse_expression(&reference.expr, context); - quote! { - ::agdb::api_def::Expression::Reference(&#expr) - } -} - -fn parse_return(expr: &Option>, context: ExpressionContext) -> TokenStream { - if let Some(expr) = expr { - let parsed = parse_expression(expr, context); - quote! { - ::agdb::api_def::Expression::Return(Some(&#parsed)) - } - } else { - quote! { - ::agdb::api_def::Expression::Return(None) - } - } -} - -fn parse_try(e: &ExprTry, context: ExpressionContext) -> TokenStream { - let parsed = parse_expression(&e.expr, context); - quote! { - ::agdb::api_def::Expression::Try(&#parsed) - } -} - -fn is_returnable(e: &Expr) -> bool { - !matches!( - e, - Expr::Return(_) - | Expr::Break(_) - | Expr::Continue(_) - | Expr::ForLoop(_) - | Expr::While(_) - | Expr::If(_) - ) -} diff --git a/agdb_derive/src/api_def/expression/array.rs b/agdb_derive/src/api_def/expression/array.rs deleted file mode 100644 index 37e71b6c..00000000 --- a/agdb_derive/src/api_def/expression/array.rs +++ /dev/null @@ -1,27 +0,0 @@ -use crate::api_def::expression; -use crate::api_def::statement::ExpressionContext; -use proc_macro2::TokenStream; -use quote::quote; -use syn::ExprArray; -use syn::ExprIndex; - -pub(crate) fn parse_array(ar: &ExprArray, context: ExpressionContext) -> TokenStream { - let elements = ar - .elems - .iter() - .map(|elem| expression::parse_expression(elem, context.inner())); - quote! { - ::agdb::api_def::Expression::Array(&[#(#elements),*]) - } -} - -pub(crate) fn parse_index(e: &ExprIndex, context: ExpressionContext) -> TokenStream { - let expr = expression::parse_expression(&e.expr, context.inner()); - let index = expression::parse_expression(&e.index, context.inner()); - quote! { - ::agdb::api_def::Expression::Index { - base: &#expr, - index: &#index, - } - } -} diff --git a/agdb_derive/src/api_def/expression/block.rs b/agdb_derive/src/api_def/expression/block.rs deleted file mode 100644 index ab394f4b..00000000 --- a/agdb_derive/src/api_def/expression/block.rs +++ /dev/null @@ -1,32 +0,0 @@ -use crate::api_def::statement; -use crate::api_def::statement::ExpressionContext; -use proc_macro2::TokenStream; -use quote::quote; -use syn::Block; - -pub(crate) fn parse_block(block: &Block, context: ExpressionContext) -> TokenStream { - let expressions = parse_block_impl(block, context); - - quote! { - ::agdb::api_def::Expression::Block(&[#(#expressions),*]) - } -} - -pub(crate) fn parse_block_impl(block: &Block, context: ExpressionContext) -> Vec { - let context = context.inner(); - block - .stmts - .iter() - .enumerate() - .map(|(i, stmt)| { - statement::parse_statement( - stmt, - if i + 1 == block.stmts.len() { - context.last() - } else { - context - }, - ) - }) - .collect() -} diff --git a/agdb_derive/src/api_def/expression/call.rs b/agdb_derive/src/api_def/expression/call.rs deleted file mode 100644 index 8f8a4c0b..00000000 --- a/agdb_derive/src/api_def/expression/call.rs +++ /dev/null @@ -1,53 +0,0 @@ -use crate::api_def::expression; -use crate::api_def::statement::ExpressionContext; -use proc_macro2::TokenStream; -use quote::quote; -use syn::ExprCall; -use syn::ExprMethodCall; - -pub(crate) fn parse_call(e: &ExprCall, context: ExpressionContext) -> TokenStream { - let function = expression::parse_expression(&e.func, context.inner()); - let args = e - .args - .iter() - .map(|arg| expression::parse_expression(arg, context.inner())); - - quote! { - ::agdb::api_def::Expression::Call { - recipient: None, - function: &#function, - args: &[#(#args),*], - } - } -} - -pub(crate) fn parse_method_call(e: &ExprMethodCall, context: ExpressionContext) -> TokenStream { - let recipient = expression::parse_expression(&e.receiver, context.inner()); - let function = &e.method; - let generics = e - .turbofish - .as_ref() - .map(|gt| { - gt.args - .iter() - .map(|ty| quote! { <#ty as ::agdb::api_def::TypeDefinition>::type_def }) - .collect::>() - }) - .unwrap_or_default(); - let args = e - .args - .iter() - .map(|arg| expression::parse_expression(arg, context.inner())); - - quote! { - ::agdb::api_def::Expression::Call { - recipient: Some(&#recipient), - function: &::agdb::api_def::Expression::Path { - ident: stringify!(#function), - parent: None, - generics: &[#(#generics),*], - }, - args: &[#(#args),*], - } - } -} diff --git a/agdb_derive/src/api_def/expression/closure.rs b/agdb_derive/src/api_def/expression/closure.rs deleted file mode 100644 index 1812ab00..00000000 --- a/agdb_derive/src/api_def/expression/closure.rs +++ /dev/null @@ -1,45 +0,0 @@ -use crate::api_def::expression; -use crate::api_def::expression::block; -use crate::api_def::expression::pattern; -use crate::api_def::function_def; -use crate::api_def::statement::ExpressionContext; -use proc_macro2::TokenStream; -use quote::quote; -use syn::Expr; -use syn::ExprClosure; - -pub(crate) fn parse_closure(e: &ExprClosure, context: ExpressionContext) -> TokenStream { - let args: Vec = e - .inputs - .iter() - .map(|arg| { - let (name, ty) = pattern::parse_pattern_to_string(arg, context); - quote! { - ::agdb::api_def::NamedType { - name: stringify!(#name), - ty: #ty, - }} - }) - .collect(); - let async_fn = e.asyncness.is_some(); - let ret = function_def::parse_ret(&e.output, &[]); - let body = parse_body(&e.body, context.inner()); - - quote! { - ::agdb::api_def::Expression::Closure(::agdb::api_def::Function { - name: "", - generics: &[], - args: &[#(#args),*], - ret: #ret, - async_fn: #async_fn, - expressions: &[#(#body),*], - }) - } -} - -fn parse_body(e: &Expr, context: ExpressionContext) -> Vec { - match e { - Expr::Block(body) => block::parse_block_impl(&body.block, context), - _ => vec![expression::parse_expression(e, context.last())], - } -} diff --git a/agdb_derive/src/api_def/expression/conditionals.rs b/agdb_derive/src/api_def/expression/conditionals.rs deleted file mode 100644 index 2164d8a9..00000000 --- a/agdb_derive/src/api_def/expression/conditionals.rs +++ /dev/null @@ -1,151 +0,0 @@ -use proc_macro2::TokenStream; -use quote::quote; -use syn::Expr; -use syn::ExprIf; -use syn::ExprMatch; -use syn::Pat; -use syn::PatOr; - -use crate::api_def::expression; -use crate::api_def::expression::block; -use crate::api_def::expression::pattern; -use crate::api_def::statement::ExpressionContext; - -pub(crate) fn parse_if(if_expr: &ExprIf, context: ExpressionContext) -> TokenStream { - let condition = expression::parse_expression(&if_expr.cond, context.inner()); - let then_branch = block::parse_block(&if_expr.then_branch, context.inner()); - - let else_branch = if let Some((_, else_expr)) = &if_expr.else_branch { - let else_tokens = match else_expr.as_ref() { - Expr::If(else_if) => parse_if(else_if, context.inner()), - Expr::Block(else_block) => block::parse_block(&else_block.block, context.inner()), - _ => panic!( - "{} Unsupported else branch in if expression", - context.fn_name - ), - }; - quote! { Some(&#else_tokens) } - } else { - quote! { None } - }; - - quote! { - ::agdb::api_def::Expression::If { - condition: &#condition, - then_branch: &#then_branch, - else_branch: #else_branch, - } - } -} - -pub(crate) fn parse_match(match_expr: &ExprMatch, context: ExpressionContext) -> TokenStream { - let subject = expression::parse_expression(&match_expr.expr, context.inner()); - let mut branches = Vec::new(); - let mut else_branch: Option = None; - - for arm in &match_expr.arms { - if is_wild(&arm.pat) { - else_branch = Some(parse_match_arm_body(&arm.body, context.inner())); - } else { - let condition = parse_match_condition(&subject, &arm.pat, context.inner()); - let condition_with_guard = if let Some(guard) = &arm.guard { - let guard_expr = expression::parse_expression(&guard.1, context.inner()); - quote! { - ::agdb::api_def::Expression::Binary { - left: &#condition, - op: ::agdb::api_def::Op::And, - right: &#guard_expr, - } - } - } else { - condition - }; - let body = parse_match_arm_body(&arm.body, context.inner()); - branches.push((condition_with_guard, body)); - } - } - - if branches.is_empty() { - else_branch.expect("Match expression must have at least one arm") - } else { - branches - .iter() - .rev() - .fold(else_branch, |else_branch, (cond, body)| { - let else_condition = if let Some(else_branch) = else_branch { - else_branch - } else { - quote! { - ::agdb::api_def::Expression::Block(&[]) - } - }; - - Some(quote! { - ::agdb::api_def::Expression::If { - condition: &#cond, - then_branch: &#body, - else_branch: Some(&#else_condition), - } - }) - }) - .expect("At least one match arm present") - } -} - -fn is_wild(pat: &Pat) -> bool { - matches!(pat, Pat::Wild(_)) -} - -fn parse_match_arm_body(body: &Expr, context: ExpressionContext) -> TokenStream { - match body { - Expr::Block(b) => block::parse_block(&b.block, context.inner()), - - // Match `()` to an empty block - Expr::Tuple(t) if t.elems.is_empty() => { - quote! { &::agdb::api_def::Expression::Block(&[]) } - } - // For other expressions, wrap in a block - expr => { - let inner = expression::parse_expression(expr, context.inner()); - quote! { &::agdb::api_def::Expression::Block(&[#inner]) } - } - } -} - -fn parse_match_condition( - subject: &TokenStream, - pat: &Pat, - context: ExpressionContext, -) -> TokenStream { - if let Pat::Or(p) = pat { - return match_or(subject, p, context); - } - - let rhs = pattern::parse_pattern(pat, context).0; - - quote! { - ::agdb::api_def::Expression::Binary { - left: &#subject, - op: ::agdb::api_def::Op::Eq, - right: &#rhs, - } - } -} - -fn match_or(subject: &TokenStream, pat_or: &PatOr, context: ExpressionContext) -> TokenStream { - let mut conds: Vec = Vec::new(); - for subpat in pat_or.cases.iter() { - conds.push(parse_match_condition(subject, subpat, context.inner())); - } - let mut iter = conds.into_iter(); - let first = iter.next().expect("Or pattern cannot be without cases"); - iter.fold(first, |acc, next| { - quote! { - ::agdb::api_def::Expression::Binary { - left: &#acc, - op: ::agdb::api_def::Op::Or, - right: &#next, - } - } - }) -} diff --git a/agdb_derive/src/api_def/expression/literal.rs b/agdb_derive/src/api_def/expression/literal.rs deleted file mode 100644 index 69f269c8..00000000 --- a/agdb_derive/src/api_def/expression/literal.rs +++ /dev/null @@ -1,34 +0,0 @@ -use crate::api_def::statement::ExpressionContext; -use proc_macro2::TokenStream; -use quote::quote; -use syn::Lit; - -pub(crate) fn parse_literal(lit: &Lit, _context: ExpressionContext) -> TokenStream { - match lit { - Lit::Str(lit_str) => { - let value = lit_str.value(); - quote! { - ::agdb::api_def::Expression::Literal(::agdb::api_def::Literal::String(stringify!(#value))) - } - } - Lit::Int(lit_int) => { - let value = lit_int.base10_parse::().unwrap(); - quote! { - ::agdb::api_def::Expression::Literal(::agdb::api_def::Literal::I64(#value)) - } - } - Lit::Float(lit_float) => { - let value = lit_float.base10_parse::().unwrap(); - quote! { - ::agdb::api_def::Expression::Literal(::agdb::api_def::Literal::F64(#value)) - } - } - Lit::Bool(lit_bool) => { - let value = lit_bool.value; - quote! { - ::agdb::api_def::Expression::Literal(::agdb::api_def::Literal::Bool(#value)) - } - } - _ => panic!("Unsupported literal: {:?}", lit), - } -} diff --git a/agdb_derive/src/api_def/expression/loops.rs b/agdb_derive/src/api_def/expression/loops.rs deleted file mode 100644 index 1c8c3e49..00000000 --- a/agdb_derive/src/api_def/expression/loops.rs +++ /dev/null @@ -1,68 +0,0 @@ -use crate::api_def::expression; -use crate::api_def::expression::block; -use crate::api_def::expression::pattern; -use crate::api_def::statement::ExpressionContext; -use proc_macro2::TokenStream; -use quote::quote; -use syn::ExprBreak; -use syn::ExprContinue; -use syn::ExprWhile; - -pub(crate) fn parse_for_loop( - for_loop: &syn::ExprForLoop, - context: ExpressionContext, -) -> TokenStream { - let (pattern, _) = pattern::parse_pattern(&for_loop.pat, context.inner()); - let iterable = expression::parse_expression(&for_loop.expr, context.inner()); - let body = block::parse_block(&for_loop.body, context.inner()); - - quote! { - ::agdb::api_def::Expression::For { - pattern: &#pattern, - iterable: &#iterable, - body: &#body, - } - } -} - -pub(crate) fn parse_break(break_expr: &ExprBreak, context: ExpressionContext) -> TokenStream { - if break_expr.expr.is_some() { - panic!("{} Break with value is not supported", context.fn_name); - } - - quote! { - ::agdb::api_def::Expression::Break - } -} - -pub(crate) fn parse_loop(loop_expr: &syn::ExprLoop, context: ExpressionContext) -> TokenStream { - let body = block::parse_block(&loop_expr.body, context.inner()); - - quote! { - ::agdb::api_def::Expression::While { - condition: &::agdb::api_def::Expression::Literal(::agdb::api_def::Literal::Bool(true)), - body: &#body, - } - } -} - -pub(crate) fn parse_while_loop(while_expr: &ExprWhile, context: ExpressionContext) -> TokenStream { - let condition = expression::parse_expression(&while_expr.cond, context.inner()); - let body = block::parse_block(&while_expr.body, context.inner()); - - quote! { - ::agdb::api_def::Expression::While { - condition: &#condition, - body: &#body, - } - } -} - -pub(crate) fn parse_continue( - _continue_expr: &ExprContinue, - _context: ExpressionContext, -) -> TokenStream { - quote! { - ::agdb::api_def::Expression::Continue - } -} diff --git a/agdb_derive/src/api_def/expression/macros.rs b/agdb_derive/src/api_def/expression/macros.rs deleted file mode 100644 index 0405e513..00000000 --- a/agdb_derive/src/api_def/expression/macros.rs +++ /dev/null @@ -1,116 +0,0 @@ -use crate::api_def::expression; -use crate::api_def::expression::path; -use crate::api_def::statement::ExpressionContext; -use proc_macro2::TokenStream; -use quote::quote; -use syn::Expr; -use syn::ExprMacro; -use syn::parse::Parser; -use syn::punctuated::Punctuated; -use syn::token::Comma; - -pub(crate) fn parse_macros(e: &ExprMacro, context: ExpressionContext) -> TokenStream { - let name = path::parse_identifier_to_string(&e.mac.path); - let args: Punctuated = Punctuated::parse_terminated - .parse2(e.mac.tokens.clone()) - .unwrap_or_default(); - - match name.as_str() { - "vec" => { - let elements = args - .iter() - .map(|arg| expression::parse_expression(arg, context.inner())); - quote! { - ::agdb::api_def::Expression::Array(&[#(#elements),*]) - } - } - "format" => { - let (format_string, args) = parse_format_string(args, context); - - quote! { - ::agdb::api_def::Expression::Format { - format_string: #format_string, - args: &[#(#args),*], - } - } - } - _ => { - panic!("Unsupported macro: {}", name); - } - } -} - -fn parse_format_string( - args: Punctuated, - context: ExpressionContext, -) -> (TokenStream, Vec) { - let mut args_iter = args.into_iter(); - let format_string_expr = args_iter.next().unwrap_or_else(|| { - panic!( - "{}: format! macro requires at least one argument", - context.fn_name - ) - }); - let format_string = extract_format_string(&format_string_expr); - extract_format_string_with_args(format_string, args_iter, context) -} - -fn extract_format_string(e: &Expr) -> String { - match e { - Expr::Lit(expr_lit) => { - if let syn::Lit::Str(lit_str) = &expr_lit.lit { - lit_str.value() - } else { - panic!("First argument to format! must be a string literal"); - } - } - _ => panic!("First argument to format! must be a string literal"), - } -} - -fn extract_format_string_with_args( - format_string: String, - mut args_iter: impl Iterator, - context: ExpressionContext, -) -> (TokenStream, Vec) { - let mut args = Vec::new(); - let mut format_iter = format_string.chars(); - let mut format_str = String::new(); - - while let Some(c) = format_iter.next() { - format_str.push(c); - - if c == '{' - && let Some(next_char) = format_iter.next() - { - if next_char == '{' { - format_str.push(next_char); - continue; //escaped brace (double brace) - } - - format_str.push('}'); - - if next_char == '}' { - let arg = args_iter - .next() - .unwrap_or_else(|| panic!("{}: not enough args", context.fn_name)); - args.push(expression::parse_expression(&arg, context)); - } else { - let mut ident = next_char.to_string(); - - for nc in format_iter.by_ref() { - if nc == '}' { - break; - } - ident.push(nc); - } - - args.push(quote! { - ::agdb::api_def::Expression::Ident(#ident) - }); - } - } - } - - (quote! { stringify!(#format_str) }, args) -} diff --git a/agdb_derive/src/api_def/expression/object.rs b/agdb_derive/src/api_def/expression/object.rs deleted file mode 100644 index 2e3a6c49..00000000 --- a/agdb_derive/src/api_def/expression/object.rs +++ /dev/null @@ -1,69 +0,0 @@ -use proc_macro2::TokenStream; -use quote::quote; -use syn::ExprField; -use syn::ExprStruct; -use syn::FieldValue; -use syn::Member; - -use crate::api_def::expression; -use crate::api_def::expression::path; -use crate::api_def::statement::ExpressionContext; - -pub(crate) fn parse_field_access(e: &ExprField, context: ExpressionContext) -> TokenStream { - let base_ts = expression::parse_expression(&e.base, context.inner()); - match &e.member { - Member::Named(ident) => { - quote! { - ::agdb::api_def::Expression::FieldAccess { - base: &#base_ts, - field: stringify!(#ident), - } - } - } - Member::Unnamed(index) => { - let index = index.index; - quote! { - ::agdb::api_def::Expression::TupleAccess { - base: &#base_ts, - index: #index, - } - } - } - } -} - -pub(crate) fn parse_tuple(e: &syn::ExprTuple, context: ExpressionContext) -> TokenStream { - let elements = e - .elems - .iter() - .map(|elem| expression::parse_expression(elem, context.inner())); - quote! { - ::agdb::api_def::Expression::Tuple(&[#(#elements),*]) - } -} - -pub(crate) fn parse_struct(e: &ExprStruct, context: ExpressionContext) -> TokenStream { - let path = path::parse_path(&e.path, context.inner()); - let fields = e - .fields - .iter() - .map(|field| parse_struct_field(field, &context)); - quote! { - ::agdb::api_def::Expression::Struct { - name: &#path, - fields: &[#(#fields),*], - } - } -} - -fn parse_struct_field(field: &FieldValue, context: &ExpressionContext) -> TokenStream { - let field_name = match &field.member { - Member::Named(ident) => ident, - Member::Unnamed(_) => panic!("Unnamed fields are not supported in struct expressions"), - }; - let field_value = expression::parse_expression(&field.expr, context.inner()); - - quote! { - (stringify!(#field_name), #field_value) - } -} diff --git a/agdb_derive/src/api_def/expression/op.rs b/agdb_derive/src/api_def/expression/op.rs deleted file mode 100644 index 5f165710..00000000 --- a/agdb_derive/src/api_def/expression/op.rs +++ /dev/null @@ -1,89 +0,0 @@ -use crate::api_def::expression; -use crate::api_def::statement::ExpressionContext; -use proc_macro2::TokenStream; -use quote::quote; -use syn::BinOp; -use syn::ExprBinary; -use syn::ExprUnary; - -pub(crate) fn parse_assign(assign: &syn::ExprAssign, context: ExpressionContext) -> TokenStream { - let left = expression::parse_expression(&assign.left, context.inner()); - let right = expression::parse_expression(&assign.right, context.inner()); - - quote! { - { - ::agdb::api_def::Expression::Assign { - target: &#left, - value: &#right, - } - } - } -} - -pub(crate) fn parse_binary_op(bin: &ExprBinary, context: ExpressionContext) -> TokenStream { - let left = expression::parse_expression(&bin.left, context.inner()); - let right = expression::parse_expression(&bin.right, context.inner()); - let op = parse_op(&bin.op); - - quote! { - ::agdb::api_def::Expression::Binary { - left: &#left, - op: #op, - right: &#right, - } - } -} - -pub(crate) fn parse_unary_op(un: &ExprUnary, context: ExpressionContext) -> TokenStream { - let expr = expression::parse_expression(&un.expr, context.inner()); - let op = match &un.op { - syn::UnOp::Deref(_) => quote! { ::agdb::api_def::Op::Deref }, - syn::UnOp::Not(_) => quote! { ::agdb::api_def::Op::Not }, - syn::UnOp::Neg(_) => quote! { ::agdb::api_def::Op::Neg }, - _ => panic!( - "{}: Unsupported unary operator: {:?}", - context.fn_name, un.op - ), - }; - - quote! { - ::agdb::api_def::Expression::Unary { - op: #op, - expr: &#expr, - } - } -} - -fn parse_op(op: &BinOp) -> TokenStream { - match op { - BinOp::Add(_) => quote! { ::agdb::api_def::Op::Add }, - BinOp::Sub(_) => quote! { ::agdb::api_def::Op::Sub }, - BinOp::Mul(_) => quote! { ::agdb::api_def::Op::Mul }, - BinOp::Div(_) => quote! { ::agdb::api_def::Op::Div }, - BinOp::Rem(_) => quote! { ::agdb::api_def::Op::Rem }, - BinOp::BitXor(_) => quote! { ::agdb::api_def::Op::BitXor }, - BinOp::BitAnd(_) => quote! { ::agdb::api_def::Op::BitAnd }, - BinOp::BitOr(_) => quote! { ::agdb::api_def::Op::BitOr }, - BinOp::Lt(_) => quote! { ::agdb::api_def::Op::Lt }, - BinOp::Gt(_) => quote! { ::agdb::api_def::Op::Gt }, - BinOp::And(_) => quote! { ::agdb::api_def::Op::And }, - BinOp::Or(_) => quote! { ::agdb::api_def::Op::Or }, - BinOp::Shl(_) => quote! { ::agdb::api_def::Op::Shl }, - BinOp::Shr(_) => quote! { ::agdb::api_def::Op::Shr }, - BinOp::Eq(_) => quote! { ::agdb::api_def::Op::Eq }, - BinOp::Le(_) => quote! { ::agdb::api_def::Op::Le }, - BinOp::Ne(_) => quote! { ::agdb::api_def::Op::Ne }, - BinOp::Ge(_) => quote! { ::agdb::api_def::Op::Ge }, - BinOp::AddAssign(_) => quote! { ::agdb::api_def::Op::AddAssign }, - BinOp::SubAssign(_) => quote! { ::agdb::api_def::Op::SubAssign }, - BinOp::MulAssign(_) => quote! { ::agdb::api_def::Op::MulAssign }, - BinOp::DivAssign(_) => quote! { ::agdb::api_def::Op::DivAssign }, - BinOp::RemAssign(_) => quote! { ::agdb::api_def::Op::RemAssign }, - BinOp::BitXorAssign(_) => quote! { ::agdb::api_def::Op::BitXorAssign }, - BinOp::BitAndAssign(_) => quote! { ::agdb::api_def::Op::BitAndAssign }, - BinOp::BitOrAssign(_) => quote! { ::agdb::api_def::Op::BitOrAssign }, - BinOp::ShlAssign(_) => quote! { ::agdb::api_def::Op::ShlAssign }, - BinOp::ShrAssign(_) => quote! { ::agdb::api_def::Op::ShrAssign }, - _ => panic!("Unsupported binary operator"), - } -} diff --git a/agdb_derive/src/api_def/expression/path.rs b/agdb_derive/src/api_def/expression/path.rs deleted file mode 100644 index c947e06a..00000000 --- a/agdb_derive/src/api_def/expression/path.rs +++ /dev/null @@ -1,61 +0,0 @@ -use crate::api_def::statement::ExpressionContext; -use proc_macro2::TokenStream; -use quote::ToTokens; -use quote::quote; -use syn::GenericArgument; -use syn::Path; -use syn::PathArguments; -use syn::PathSegment; - -pub(crate) fn parse_path(path: &Path, _context: ExpressionContext) -> TokenStream { - let mut iter = path.segments.iter(); - let first = iter.next().expect("path should have at least one segment"); - let first_segment = parse_path_segment(first, quote! { None }); - - iter.fold(first_segment, |path, segment| { - parse_path_segment(segment, quote! { Some(&#path) }) - }) -} - -fn parse_path_segment(path: &PathSegment, parent: TokenStream) -> TokenStream { - let ident = &path.ident; - - let generics = match &path.arguments { - PathArguments::AngleBracketed(args) => { - args.args - .iter() - .filter_map(|ga| { - match ga { - GenericArgument::Type(ty) => { - Some(quote! { <#ty as ::agdb::api_def::TypeDefinition>::type_def }) - } - // Skip lifetimes, const generics, bindings, constraints for now - _ => None, - } - }) - .collect::>() - } - PathArguments::Parenthesized(args) => args - .inputs - .iter() - .map(|ty| quote! { <#ty as ::agdb::api_def::TypeDefinition>::type_def }) - .collect::>(), - PathArguments::None => Vec::new(), - }; - - quote! { - ::agdb::api_def::Expression::Path { - ident: stringify!(#ident), - parent: #parent, - generics: &[#(#generics),*], - } - } -} - -pub(crate) fn parse_identifier_to_string(path: &Path) -> String { - path.segments - .last() - .expect("path should not be empty") - .to_token_stream() - .to_string() -} diff --git a/agdb_derive/src/api_def/expression/pattern.rs b/agdb_derive/src/api_def/expression/pattern.rs deleted file mode 100644 index 9cea956f..00000000 --- a/agdb_derive/src/api_def/expression/pattern.rs +++ /dev/null @@ -1,241 +0,0 @@ -use proc_macro2::TokenStream; -use quote::quote; -use syn::Expr; -use syn::Pat; -use syn::PatIdent; -use syn::PatOr; -use syn::PatSlice; -use syn::PatTuple; -use syn::PatTupleStruct; - -use crate::api_def::expression; -use crate::api_def::expression::literal; -use crate::api_def::expression::path; -use crate::api_def::statement::ExpressionContext; -use crate::api_def::type_def; - -pub(crate) fn parse_let(pat: &Pat, init: Option<&Expr>, context: ExpressionContext) -> TokenStream { - let (name, ty) = parse_pattern(pat, context.inner()); - let value = if let Some(init) = init { - let expr = expression::parse_expression(init, context.inner()); - quote! { Some(&#expr) } - } else { - quote! { None } - }; - - quote! { - ::agdb::api_def::Expression::Let { - name: &#name, - ty: #ty, - value: #value, - } - } -} - -pub(crate) fn parse_pattern(pat: &Pat, context: ExpressionContext) -> (TokenStream, TokenStream) { - match pat { - Pat::Ident(p) => ident(p), - Pat::Lit(p) => literal(context, p), - Pat::Type(p) => typed(p, context), - Pat::Or(p) => or(p, context), - Pat::Paren(p) => parse_pattern(&p.pat, context), - Pat::Path(p) => path(p, context), - Pat::Reference(p) => parse_pattern(&p.pat, context), - Pat::Slice(p) => slice(p, context), - Pat::Struct(p) => struct_pattern(p, context), - Pat::Tuple(p) => tuple(p, context), - Pat::TupleStruct(p) => tuple_struct(p, context), - Pat::Wild(_) => wild(), - // 1..=10 - Pat::Range(_) //todo? - // Tuple(expr, expr2, ...) - | Pat::Rest(_) //todo? - | Pat::Const(_) - | Pat::Macro(_) - | Pat::Verbatim(_) - | _ => panic!("Unsupported pattern in {}: {:?}", context.fn_name, pat), - } -} - -fn literal(context: ExpressionContext<'_>, p: &syn::PatLit) -> (TokenStream, TokenStream) { - let lit_expr = literal::parse_literal(&p.lit, context); - ( - quote! { - #lit_expr - }, - quote! { - None - }, - ) -} - -fn tuple_struct(p: &PatTupleStruct, context: ExpressionContext) -> (TokenStream, TokenStream) { - let path_expr = path::parse_path(&p.path, context.inner()); - let elems = p.elems.iter().map(|elem| parse_pattern(elem, context).0); - ( - quote! { - ::agdb::api_def::Expression::TupleStruct { - name: &#path_expr, - expressions: &[#(#elems),*], - } - }, - quote! { - None - }, - ) -} - -fn tuple(p: &PatTuple, context: ExpressionContext) -> (TokenStream, TokenStream) { - let elems = p.elems.iter().map(|elem| parse_pattern(elem, context).0); - ( - quote! { - ::agdb::api_def::Expression::Tuple(&[#(#elems),*]) - }, - quote! { - None - }, - ) -} - -fn struct_pattern(p: &syn::PatStruct, context: ExpressionContext) -> (TokenStream, TokenStream) { - let path_expr = path::parse_path(&p.path, context.inner()); - let fields = p.fields.iter().map(|field| { - let (field_name, _) = parse_pattern(&field.pat, context.inner()); - quote! { - #field_name - } - }); - ( - quote! { - ::agdb::api_def::Expression::StructPattern { - name: &#path_expr, - fields: &[#(#fields),*], - } - }, - quote! { - None - }, - ) -} - -fn slice(p: &PatSlice, context: ExpressionContext) -> (TokenStream, TokenStream) { - let elems = p.elems.iter().map(|elem| parse_pattern(elem, context).0); - ( - quote! { - ::agdb::api_def::Expression::Array(&[#(#elems),*]) - }, - quote! { - None - }, - ) -} - -fn path(p: &syn::PatPath, context: ExpressionContext) -> (TokenStream, TokenStream) { - let path_str = path::parse_path(&p.path, context.inner()); - ( - quote! { - #path_str - }, - quote! { - None - }, - ) -} - -fn wild() -> (TokenStream, TokenStream) { - ( - quote! { - ::agdb::api_def::Expression::Wild - }, - quote! { - None - }, - ) -} - -fn typed(pat_type: &syn::PatType, context: ExpressionContext) -> (TokenStream, TokenStream) { - let (name, _) = parse_pattern(&pat_type.pat, context); - let ty = type_def::parse_type(&pat_type.ty, context.generics); - ( - name, - quote! { - Some(#ty) - }, - ) -} - -fn ident(pat_ident: &PatIdent) -> (TokenStream, TokenStream) { - let name = &pat_ident.ident; - ( - quote! { - ::agdb::api_def::Expression::Ident(stringify!(#name)) - }, - quote! { - None - }, - ) -} - -fn or(pat_or: &PatOr, context: ExpressionContext) -> (TokenStream, TokenStream) { - let mut conds: Vec = Vec::new(); - for subpat in pat_or.cases.iter() { - conds.push(parse_pattern(subpat, context.inner()).0); - } - let mut iter = conds.into_iter(); - let first = iter.next().expect("Or pattern cannot be without cases"); - ( - iter.fold(first, |acc, next| { - quote! { - ::agdb::api_def::Expression::Binary { - left: &#acc, - op: ::agdb::api_def::Op::Or, - right: &#next, - } - } - }), - quote! { - None - }, - ) -} - -pub(crate) fn parse_pattern_to_string( - pat: &Pat, - context: ExpressionContext, -) -> (TokenStream, TokenStream) { - match pat { - Pat::Ident(pat_ident) => { - let name = &pat_ident.ident; - ( - quote! { - #name - }, - quote! { - None - }, - ) - } - Pat::Type(pat_type) => { - let (name, _) = parse_pattern_to_string(&pat_type.pat, context); - let ty = type_def::parse_type(&pat_type.ty, context.generics); - ( - name, - quote! { - Some(#ty) - }, - ) - } - Pat::Wild(_) => ( - quote! { - "_" - }, - quote! { - None - }, - ), - _ => panic!( - "Unsupported pattern to string in {}: {:?}", - context.fn_name, pat - ), - } -} diff --git a/agdb_derive/src/api_def/expression_parser.rs b/agdb_derive/src/api_def/expression_parser.rs new file mode 100644 index 00000000..138ae8ed --- /dev/null +++ b/agdb_derive/src/api_def/expression_parser.rs @@ -0,0 +1,1016 @@ +use crate::api_def::generics_parser; +use crate::api_def::generics_parser::Generic; +use proc_macro2::TokenStream as TokenStream2; +use quote::ToTokens; +use quote::quote; +use syn::BinOp; +use syn::Block; +use syn::Expr; +use syn::ExprArray; +use syn::ExprBinary; +use syn::ExprCall; +use syn::ExprClosure; +use syn::ExprField; +use syn::ExprIndex; +use syn::ExprMacro; +use syn::ExprMethodCall; +use syn::ExprReference; +use syn::ExprStruct; +use syn::ExprTry; +use syn::ExprUnary; +use syn::FieldValue; +use syn::GenericArgument; +use syn::Lit; +use syn::Member; +use syn::Pat; +use syn::PatOr; +use syn::Path; +use syn::PathArguments; +use syn::PathSegment; +use syn::ReturnType; +use syn::Stmt; + +// --------------------------------------------------------------------------- +// Public entry points +// --------------------------------------------------------------------------- + +/// Parse a block into a list of expression token-streams (one per statement). +pub(crate) fn parse_block_stmts(block: &Block, generics: &[Generic]) -> Vec { + block + .stmts + .iter() + .enumerate() + .map(|(i, stmt)| { + let last = i + 1 == block.stmts.len(); + parse_statement(stmt, generics, last) + }) + .collect() +} + +/// Parse a block into an `::agdb::api_def::Expression::Block(...)`. +pub(crate) fn parse_block(block: &Block, generics: &[Generic]) -> TokenStream2 { + let expressions = parse_block_stmts(block, generics); + quote! { + ::agdb::api_def::Expression::Block(&[#(#expressions),*]) + } +} + +/// Parse a single `syn::Expr` into a `TokenStream2` that constructs an +/// `::agdb::api_def::Expression`. +pub(crate) fn parse_expression(e: &Expr, generics: &[Generic]) -> TokenStream2 { + match e { + Expr::Array(e) => parse_array(e, generics), + Expr::Assign(e) => parse_assign(e, generics), + Expr::Async(e) => parse_block(&e.block, generics), + Expr::Await(e) => parse_await(e, generics), + Expr::Binary(e) => parse_binary(e, generics), + Expr::Block(e) => parse_block(&e.block, generics), + Expr::Break(_) => quote! { ::agdb::api_def::Expression::Break }, + Expr::Call(e) => parse_call(e, generics), + Expr::Cast(e) => parse_expression(&e.expr, generics), + Expr::Closure(e) => parse_closure(e, generics), + Expr::Const(e) => parse_block(&e.block, generics), + Expr::Continue(_) => quote! { ::agdb::api_def::Expression::Continue }, + Expr::Field(e) => parse_field_access(e, generics), + Expr::ForLoop(e) => parse_for_loop(e, generics), + Expr::Group(e) => parse_expression(&e.expr, generics), + Expr::If(e) => parse_if(e, generics), + Expr::Index(e) => parse_index(e, generics), + Expr::Infer(_) => quote! { ::agdb::api_def::Expression::Wild }, + Expr::Let(e) => parse_let_expr(e, generics), + Expr::Lit(e) => parse_literal(&e.lit), + Expr::Loop(e) => parse_loop(e, generics), + Expr::Macro(e) => parse_macro(e, generics), + Expr::Match(e) => parse_match(e, generics), + Expr::MethodCall(e) => parse_method_call(e, generics), + Expr::Paren(e) => parse_expression(&e.expr, generics), + Expr::Path(e) => parse_path(&e.path), + Expr::Reference(e) => parse_reference(e, generics), + Expr::Return(e) => parse_return(e, generics), + Expr::Struct(e) => parse_struct(e, generics), + Expr::Try(e) => parse_try(e, generics), + Expr::Tuple(e) => parse_tuple(e, generics), + Expr::Unary(e) => parse_unary(e, generics), + Expr::While(e) => parse_while(e, generics), + _ => panic!("Unsupported expression: {}", e.to_token_stream()), + } +} + +// --------------------------------------------------------------------------- +// Statements +// --------------------------------------------------------------------------- + +fn parse_statement(stmt: &Stmt, generics: &[Generic], last: bool) -> TokenStream2 { + match stmt { + Stmt::Local(local) => parse_local(local, generics), + Stmt::Expr(expr, semi) => { + let parsed = parse_expression(expr, generics); + if last && semi.is_none() && is_returnable(expr) { + quote! { ::agdb::api_def::Expression::Return(Some(&#parsed)) } + } else { + parsed + } + } + Stmt::Item(_) => quote! { ::agdb::api_def::Expression::Block(&[]) }, + Stmt::Macro(m) => parse_stmt_macro(m, generics), + } +} + +fn parse_local(local: &syn::Local, generics: &[Generic]) -> TokenStream2 { + let (name, ty) = parse_pattern(&local.pat, generics); + let value = if let Some(init) = &local.init { + let expr = parse_expression(&init.expr, generics); + quote! { Some(&#expr) } + } else { + quote! { None } + }; + + quote! { + ::agdb::api_def::Expression::Let { + name: &#name, + ty: #ty, + value: #value, + } + } +} + +fn parse_stmt_macro(m: &syn::StmtMacro, generics: &[Generic]) -> TokenStream2 { + let name = path_to_string(&m.mac.path); + parse_macro_by_name(&name, &m.mac.tokens, generics) +} + +fn is_returnable(e: &Expr) -> bool { + !matches!( + e, + Expr::Return(_) + | Expr::Break(_) + | Expr::Continue(_) + | Expr::ForLoop(_) + | Expr::While(_) + | Expr::Loop(_) + | Expr::Match(_) + | Expr::If(_) + ) +} + +// --------------------------------------------------------------------------- +// Array / Index +// --------------------------------------------------------------------------- + +fn parse_array(e: &ExprArray, generics: &[Generic]) -> TokenStream2 { + let elements = e.elems.iter().map(|elem| parse_expression(elem, generics)); + quote! { + ::agdb::api_def::Expression::Array(&[#(#elements),*]) + } +} + +fn parse_index(e: &ExprIndex, generics: &[Generic]) -> TokenStream2 { + let base = parse_expression(&e.expr, generics); + let index = parse_expression(&e.index, generics); + quote! { + ::agdb::api_def::Expression::Index { + base: &#base, + index: &#index, + } + } +} + +// --------------------------------------------------------------------------- +// Assign +// --------------------------------------------------------------------------- + +fn parse_assign(e: &syn::ExprAssign, generics: &[Generic]) -> TokenStream2 { + let target = parse_expression(&e.left, generics); + let value = parse_expression(&e.right, generics); + quote! { + ::agdb::api_def::Expression::Assign { + target: &#target, + value: &#value, + } + } +} + +// --------------------------------------------------------------------------- +// Await +// --------------------------------------------------------------------------- + +fn parse_await(e: &syn::ExprAwait, generics: &[Generic]) -> TokenStream2 { + let expr = parse_expression(&e.base, generics); + quote! { + ::agdb::api_def::Expression::Await(&#expr) + } +} + +// --------------------------------------------------------------------------- +// Binary / Unary / Op +// --------------------------------------------------------------------------- + +fn parse_binary(e: &ExprBinary, generics: &[Generic]) -> TokenStream2 { + let left = parse_expression(&e.left, generics); + let right = parse_expression(&e.right, generics); + let op = parse_binop(&e.op); + quote! { + ::agdb::api_def::Expression::Binary { + op: #op, + left: &#left, + right: &#right, + } + } +} + +fn parse_unary(e: &ExprUnary, generics: &[Generic]) -> TokenStream2 { + let expr = parse_expression(&e.expr, generics); + let op = match &e.op { + syn::UnOp::Deref(_) => quote! { ::agdb::api_def::Op::Deref }, + syn::UnOp::Not(_) => quote! { ::agdb::api_def::Op::Not }, + syn::UnOp::Neg(_) => quote! { ::agdb::api_def::Op::Neg }, + _ => panic!("Unsupported unary operator: {:?}", e.op), + }; + quote! { + ::agdb::api_def::Expression::Unary { + op: #op, + expr: &#expr, + } + } +} + +fn parse_binop(op: &BinOp) -> TokenStream2 { + match op { + BinOp::Add(_) => quote! { ::agdb::api_def::Op::Add }, + BinOp::Sub(_) => quote! { ::agdb::api_def::Op::Sub }, + BinOp::Mul(_) => quote! { ::agdb::api_def::Op::Mul }, + BinOp::Div(_) => quote! { ::agdb::api_def::Op::Div }, + BinOp::Rem(_) => quote! { ::agdb::api_def::Op::Rem }, + BinOp::BitXor(_) => quote! { ::agdb::api_def::Op::BitXor }, + BinOp::BitAnd(_) => quote! { ::agdb::api_def::Op::BitAnd }, + BinOp::BitOr(_) => quote! { ::agdb::api_def::Op::BitOr }, + BinOp::Lt(_) => quote! { ::agdb::api_def::Op::Lt }, + BinOp::Gt(_) => quote! { ::agdb::api_def::Op::Gt }, + BinOp::And(_) => quote! { ::agdb::api_def::Op::And }, + BinOp::Or(_) => quote! { ::agdb::api_def::Op::Or }, + BinOp::Shl(_) => quote! { ::agdb::api_def::Op::Shl }, + BinOp::Shr(_) => quote! { ::agdb::api_def::Op::Shr }, + BinOp::Eq(_) => quote! { ::agdb::api_def::Op::Eq }, + BinOp::Le(_) => quote! { ::agdb::api_def::Op::Le }, + BinOp::Ne(_) => quote! { ::agdb::api_def::Op::Ne }, + BinOp::Ge(_) => quote! { ::agdb::api_def::Op::Ge }, + BinOp::AddAssign(_) => quote! { ::agdb::api_def::Op::AddAssign }, + BinOp::SubAssign(_) => quote! { ::agdb::api_def::Op::SubAssign }, + BinOp::MulAssign(_) => quote! { ::agdb::api_def::Op::MulAssign }, + BinOp::DivAssign(_) => quote! { ::agdb::api_def::Op::DivAssign }, + BinOp::RemAssign(_) => quote! { ::agdb::api_def::Op::RemAssign }, + BinOp::BitXorAssign(_) => quote! { ::agdb::api_def::Op::BitXorAssign }, + BinOp::BitAndAssign(_) => quote! { ::agdb::api_def::Op::BitAndAssign }, + BinOp::BitOrAssign(_) => quote! { ::agdb::api_def::Op::BitOrAssign }, + BinOp::ShlAssign(_) => quote! { ::agdb::api_def::Op::ShlAssign }, + BinOp::ShrAssign(_) => quote! { ::agdb::api_def::Op::ShrAssign }, + _ => panic!("Unsupported binary operator"), + } +} + +// --------------------------------------------------------------------------- +// Call / MethodCall +// --------------------------------------------------------------------------- + +fn parse_call(e: &ExprCall, generics: &[Generic]) -> TokenStream2 { + let function = parse_expression(&e.func, generics); + let args = e.args.iter().map(|arg| parse_expression(arg, generics)); + quote! { + ::agdb::api_def::Expression::Call { + recipient: None, + function: &#function, + args: &[#(#args),*], + } + } +} + +fn parse_method_call(e: &ExprMethodCall, generics: &[Generic]) -> TokenStream2 { + let recipient = parse_expression(&e.receiver, generics); + let method = &e.method; + let turbofish_generics = e + .turbofish + .as_ref() + .map(|gt| { + gt.args + .iter() + .filter_map(|ga| match ga { + GenericArgument::Type(ty) => Some(quote! { <#ty as ::agdb::api_def::TypeDefinition>::type_def }), + _ => None, + }) + .collect::>() + }) + .unwrap_or_default(); + let args = e.args.iter().map(|arg| parse_expression(arg, generics)); + quote! { + ::agdb::api_def::Expression::Call { + recipient: Some(&#recipient), + function: &::agdb::api_def::Expression::Path { + ident: stringify!(#method), + parent: None, + generics: &[#(#turbofish_generics),*], + }, + args: &[#(#args),*], + } + } +} + +// --------------------------------------------------------------------------- +// Closure +// --------------------------------------------------------------------------- + +fn parse_closure(e: &ExprClosure, generics: &[Generic]) -> TokenStream2 { + let args: Vec = e + .inputs + .iter() + .map(|pat| { + let (name_tokens, ty_tokens) = parse_closure_arg(pat, generics); + quote! { + ::agdb::api_def::NamedType { + name: stringify!(#name_tokens), + ty: Some(#ty_tokens), + } + } + }) + .collect(); + let async_fn = e.asyncness.is_some(); + let ret = parse_return_type(&e.output, generics); + let body = match e.body.as_ref() { + Expr::Block(body) => parse_block_stmts(&body.block, generics), + other => { + let expr = parse_expression(other, generics); + vec![quote! { ::agdb::api_def::Expression::Return(Some(&#expr)) }] + } + }; + + quote! { + ::agdb::api_def::Expression::Closure(::agdb::api_def::Function { + name: "", + generics: &[], + args: &[#(#args),*], + ret: #ret, + async_fn: #async_fn, + body: &[#(#body),*], + }) + } +} + +fn parse_closure_arg(pat: &Pat, generics: &[Generic]) -> (TokenStream2, TokenStream2) { + match pat { + Pat::Type(p) => { + let name = extract_pat_ident(&p.pat); + let ty = generics_parser::parse_type(&p.ty, generics); + (quote! { #name }, ty) + } + Pat::Ident(p) => { + let name = &p.ident; + (quote! { #name }, quote! { <() as ::agdb::api_def::TypeDefinition>::type_def }) + } + _ => panic!( + "Unsupported closure argument pattern: {}", + pat.to_token_stream() + ), + } +} + +fn extract_pat_ident(pat: &Pat) -> &syn::Ident { + match pat { + Pat::Ident(p) => &p.ident, + _ => panic!( + "Expected identifier pattern, got: {}", + pat.to_token_stream() + ), + } +} + +fn parse_return_type(ret: &ReturnType, generics: &[Generic]) -> TokenStream2 { + match ret { + ReturnType::Default => quote! { <() as ::agdb::api_def::TypeDefinition>::type_def }, + ReturnType::Type(_, ty) => generics_parser::parse_type(ty, generics), + } +} + +// --------------------------------------------------------------------------- +// Field access / Tuple access +// --------------------------------------------------------------------------- + +fn parse_field_access(e: &ExprField, generics: &[Generic]) -> TokenStream2 { + let base = parse_expression(&e.base, generics); + match &e.member { + Member::Named(ident) => { + quote! { + ::agdb::api_def::Expression::FieldAccess { + base: &#base, + field: stringify!(#ident), + } + } + } + Member::Unnamed(index) => { + let idx = index.index; + quote! { + ::agdb::api_def::Expression::TupleAccess { + base: &#base, + index: #idx, + } + } + } + } +} + +// --------------------------------------------------------------------------- +// Loops +// --------------------------------------------------------------------------- + +fn parse_for_loop(e: &syn::ExprForLoop, generics: &[Generic]) -> TokenStream2 { + let (pattern, _) = parse_pattern(&e.pat, generics); + let iterable = parse_expression(&e.expr, generics); + let body = parse_block(&e.body, generics); + quote! { + ::agdb::api_def::Expression::For { + pattern: &#pattern, + iterable: &#iterable, + body: &#body, + } + } +} + +fn parse_loop(e: &syn::ExprLoop, generics: &[Generic]) -> TokenStream2 { + let body = parse_block(&e.body, generics); + quote! { + ::agdb::api_def::Expression::While { + condition: &::agdb::api_def::Expression::Literal(::agdb::api_def::LiteralValue::Bool(true)), + body: &#body, + } + } +} + +fn parse_while(e: &syn::ExprWhile, generics: &[Generic]) -> TokenStream2 { + let condition = parse_expression(&e.cond, generics); + let body = parse_block(&e.body, generics); + quote! { + ::agdb::api_def::Expression::While { + condition: &#condition, + body: &#body, + } + } +} + +// --------------------------------------------------------------------------- +// If / Match +// --------------------------------------------------------------------------- + +fn parse_if(e: &syn::ExprIf, generics: &[Generic]) -> TokenStream2 { + let condition = parse_expression(&e.cond, generics); + let then_branch = parse_block(&e.then_branch, generics); + + let else_branch = if let Some((_, else_expr)) = &e.else_branch { + let else_tokens = match else_expr.as_ref() { + Expr::If(else_if) => parse_if(else_if, generics), + Expr::Block(else_block) => parse_block(&else_block.block, generics), + _ => panic!("Unsupported else branch"), + }; + quote! { Some(&#else_tokens) } + } else { + quote! { None } + }; + + quote! { + ::agdb::api_def::Expression::If { + condition: &#condition, + then_branch: &#then_branch, + else_branch: #else_branch, + } + } +} + +fn parse_match(e: &syn::ExprMatch, generics: &[Generic]) -> TokenStream2 { + let subject = parse_expression(&e.expr, generics); + let mut branches = Vec::new(); + let mut else_branch: Option = None; + + for arm in &e.arms { + if matches!(&arm.pat, Pat::Wild(_)) { + else_branch = Some(parse_match_arm_body(&arm.body, generics)); + } else { + let condition = parse_match_condition(&subject, &arm.pat, generics); + let condition_with_guard = if let Some((_, guard)) = &arm.guard { + let guard_expr = parse_expression(guard, generics); + quote! { + ::agdb::api_def::Expression::Binary { + op: ::agdb::api_def::Op::And, + left: &#condition, + right: &#guard_expr, + } + } + } else { + condition + }; + let body = parse_match_arm_body(&arm.body, generics); + branches.push((condition_with_guard, body)); + } + } + + if branches.is_empty() { + else_branch.expect("Match expression must have at least one arm") + } else { + branches + .iter() + .rev() + .fold(else_branch, |else_br, (cond, body)| { + let else_part = if let Some(eb) = else_br { + eb + } else { + quote! { ::agdb::api_def::Expression::Block(&[]) } + }; + Some(quote! { + ::agdb::api_def::Expression::If { + condition: &#cond, + then_branch: &#body, + else_branch: Some(&#else_part), + } + }) + }) + .expect("At least one match arm present") + } +} + +fn parse_match_arm_body(body: &Expr, generics: &[Generic]) -> TokenStream2 { + match body { + Expr::Block(b) => parse_block(&b.block, generics), + Expr::Tuple(t) if t.elems.is_empty() => { + quote! { ::agdb::api_def::Expression::Block(&[]) } + } + expr => { + let inner = parse_expression(expr, generics); + quote! { ::agdb::api_def::Expression::Block(&[#inner]) } + } + } +} + +fn parse_match_condition(subject: &TokenStream2, pat: &Pat, generics: &[Generic]) -> TokenStream2 { + if let Pat::Or(p) = pat { + return parse_match_or(subject, p, generics); + } + + let (rhs, _) = parse_pattern(pat, generics); + quote! { + ::agdb::api_def::Expression::Binary { + op: ::agdb::api_def::Op::Eq, + left: &#subject, + right: &#rhs, + } + } +} + +fn parse_match_or(subject: &TokenStream2, pat_or: &PatOr, generics: &[Generic]) -> TokenStream2 { + let conds: Vec = pat_or + .cases + .iter() + .map(|subpat| parse_match_condition(subject, subpat, generics)) + .collect(); + let mut iter = conds.into_iter(); + let first = iter.next().expect("Or pattern must have cases"); + iter.fold(first, |acc, next| { + quote! { + ::agdb::api_def::Expression::Binary { + op: ::agdb::api_def::Op::Or, + left: &#acc, + right: &#next, + } + } + }) +} + +// --------------------------------------------------------------------------- +// Let (expression form, e.g. `if let`) +// --------------------------------------------------------------------------- + +fn parse_let_expr(e: &syn::ExprLet, generics: &[Generic]) -> TokenStream2 { + let (name, ty) = parse_pattern(&e.pat, generics); + let value = parse_expression(&e.expr, generics); + quote! { + ::agdb::api_def::Expression::Let { + name: &#name, + ty: #ty, + value: Some(&#value), + } + } +} + +// --------------------------------------------------------------------------- +// Literal +// --------------------------------------------------------------------------- + +fn parse_literal(lit: &Lit) -> TokenStream2 { + match lit { + Lit::Str(s) => { + let value = s.value(); + quote! { + ::agdb::api_def::Expression::Literal(::agdb::api_def::LiteralValue::Str(#value)) + } + } + Lit::Int(i) => { + let suffix = i.suffix(); + match suffix { + "i8" => { + let v = i.base10_parse::().unwrap(); + quote! { ::agdb::api_def::Expression::Literal(::agdb::api_def::LiteralValue::I8(#v)) } + } + "i16" => { + let v = i.base10_parse::().unwrap(); + quote! { ::agdb::api_def::Expression::Literal(::agdb::api_def::LiteralValue::I16(#v)) } + } + "i32" | "" => { + let v = i.base10_parse::().unwrap(); + quote! { ::agdb::api_def::Expression::Literal(::agdb::api_def::LiteralValue::I32(#v)) } + } + "u8" => { + let v = i.base10_parse::().unwrap(); + quote! { ::agdb::api_def::Expression::Literal(::agdb::api_def::LiteralValue::U8(#v)) } + } + "u16" => { + let v = i.base10_parse::().unwrap(); + quote! { ::agdb::api_def::Expression::Literal(::agdb::api_def::LiteralValue::U16(#v)) } + } + "u32" => { + let v = i.base10_parse::().unwrap(); + quote! { ::agdb::api_def::Expression::Literal(::agdb::api_def::LiteralValue::U32(#v)) } + } + "u64" => { + let v = i.base10_parse::().unwrap(); + quote! { ::agdb::api_def::Expression::Literal(::agdb::api_def::LiteralValue::U64(#v)) } + } + "usize" => { + let v = i.base10_parse::().unwrap(); + quote! { ::agdb::api_def::Expression::Literal(::agdb::api_def::LiteralValue::Usize(#v)) } + } + _ => panic!("Unsupported integer suffix: {suffix}"), + } + } + Lit::Float(f) => { + let suffix = f.suffix(); + match suffix { + "f32" => { + let v = f.base10_parse::().unwrap(); + quote! { ::agdb::api_def::Expression::Literal(::agdb::api_def::LiteralValue::F32(#v)) } + } + "f64" | "" => { + let v = f.base10_parse::().unwrap(); + quote! { ::agdb::api_def::Expression::Literal(::agdb::api_def::LiteralValue::F64(#v)) } + } + _ => panic!("Unsupported float suffix: {suffix}"), + } + } + Lit::Bool(b) => { + let value = b.value; + quote! { + ::agdb::api_def::Expression::Literal(::agdb::api_def::LiteralValue::Bool(#value)) + } + } + Lit::Char(c) => { + let value = c.value().to_string(); + quote! { + ::agdb::api_def::Expression::Literal(::agdb::api_def::LiteralValue::Str(#value)) + } + } + _ => panic!("Unsupported literal: {:?}", lit), + } +} + +// --------------------------------------------------------------------------- +// Macro (format!, vec!, etc.) +// --------------------------------------------------------------------------- + +fn parse_macro(e: &ExprMacro, generics: &[Generic]) -> TokenStream2 { + let name = path_to_string(&e.mac.path); + parse_macro_by_name(&name, &e.mac.tokens, generics) +} + +fn parse_macro_by_name( + name: &str, + tokens: &proc_macro2::TokenStream, + generics: &[Generic], +) -> TokenStream2 { + let args: syn::punctuated::Punctuated = syn::parse::Parser::parse2( + syn::punctuated::Punctuated::parse_terminated, + tokens.clone(), + ) + .unwrap_or_default(); + + match name { + "vec" => { + let elements = args.iter().map(|arg| parse_expression(arg, generics)); + quote! { + ::agdb::api_def::Expression::Array(&[#(#elements),*]) + } + } + "format" => { + let mut args_iter = args.into_iter(); + let format_string_expr = args_iter + .next() + .expect("format! requires at least one argument"); + let format_string = extract_format_string(&format_string_expr); + let (fmt_str, fmt_args) = + extract_format_parts(&format_string, &mut args_iter, generics); + quote! { + ::agdb::api_def::Expression::Format { + format_string: #fmt_str, + args: &[#(#fmt_args),*], + } + } + } + // Common macros treated as calls with string arguments + "panic" | "todo" | "unimplemented" | "println" | "eprintln" | "dbg" | "assert" + | "assert_eq" | "assert_ne" | "debug_assert" | "debug_assert_eq" | "debug_assert_ne" + | "unreachable" | "write" | "writeln" => { + let macro_args = args.iter().map(|arg| parse_expression(arg, generics)); + quote! { + ::agdb::api_def::Expression::Call { + recipient: None, + function: &::agdb::api_def::Expression::Path { + ident: #name, + parent: None, + generics: &[], + }, + args: &[#(#macro_args),*], + } + } + } + _ => panic!("Unsupported macro: {name}"), + } +} + +fn extract_format_string(e: &Expr) -> String { + match e { + Expr::Lit(expr_lit) => { + if let Lit::Str(lit_str) = &expr_lit.lit { + lit_str.value() + } else { + panic!("First argument to format! must be a string literal"); + } + } + _ => panic!("First argument to format! must be a string literal"), + } +} + +fn extract_format_parts( + format_string: &str, + args_iter: &mut impl Iterator, + generics: &[Generic], +) -> (TokenStream2, Vec) { + let mut args = Vec::new(); + let mut fmt_str = String::new(); + let mut chars = format_string.chars(); + + while let Some(c) = chars.next() { + fmt_str.push(c); + + if c == '{' + && let Some(next) = chars.next() + { + if next == '{' { + fmt_str.push(next); + continue; // escaped brace + } + + fmt_str.push('}'); + + if next == '}' { + // positional argument + let arg = args_iter + .next() + .expect("not enough arguments for format string"); + args.push(parse_expression(&arg, generics)); + } else { + // named argument + let mut ident = next.to_string(); + for nc in chars.by_ref() { + if nc == '}' { + break; + } + ident.push(nc); + } + args.push(quote! { ::agdb::api_def::Expression::Ident(#ident) }); + } + } + } + + (quote! { #fmt_str }, args) +} + +// --------------------------------------------------------------------------- +// Path +// --------------------------------------------------------------------------- + +fn parse_path(path: &Path) -> TokenStream2 { + let mut iter = path.segments.iter(); + let first = iter.next().expect("path should have at least one segment"); + + // Single-segment path with no generics => Ident + if path.segments.len() == 1 && matches!(first.arguments, PathArguments::None) { + let ident = &first.ident; + return quote! { + ::agdb::api_def::Expression::Ident(stringify!(#ident)) + }; + } + + let first_segment = parse_path_segment(first, quote! { None }); + iter.fold(first_segment, |parent, segment| { + parse_path_segment(segment, quote! { Some(&#parent) }) + }) +} + +fn parse_path_segment(segment: &PathSegment, parent: TokenStream2) -> TokenStream2 { + let ident = &segment.ident; + let generics = match &segment.arguments { + PathArguments::AngleBracketed(args) => args + .args + .iter() + .filter_map(|ga| match ga { + GenericArgument::Type(ty) => Some(quote! { <#ty as ::agdb::api_def::TypeDefinition>::type_def }), + _ => None, + }) + .collect::>(), + PathArguments::Parenthesized(args) => args + .inputs + .iter() + .map(|ty| quote! { <#ty as ::agdb::api_def::TypeDefinition>::type_def }) + .collect::>(), + PathArguments::None => Vec::new(), + }; + + quote! { + ::agdb::api_def::Expression::Path { + ident: stringify!(#ident), + parent: #parent, + generics: &[#(#generics),*], + } + } +} + +fn path_to_string(path: &Path) -> String { + path.segments + .last() + .expect("path should not be empty") + .ident + .to_string() +} + +// --------------------------------------------------------------------------- +// Reference / Return / Try +// --------------------------------------------------------------------------- + +fn parse_reference(e: &ExprReference, generics: &[Generic]) -> TokenStream2 { + let expr = parse_expression(&e.expr, generics); + quote! { + ::agdb::api_def::Expression::Reference(&#expr) + } +} + +fn parse_return(e: &syn::ExprReturn, generics: &[Generic]) -> TokenStream2 { + if let Some(expr) = &e.expr { + let parsed = parse_expression(expr, generics); + quote! { + ::agdb::api_def::Expression::Return(Some(&#parsed)) + } + } else { + quote! { + ::agdb::api_def::Expression::Return(None) + } + } +} + +fn parse_try(e: &ExprTry, generics: &[Generic]) -> TokenStream2 { + let expr = parse_expression(&e.expr, generics); + quote! { + ::agdb::api_def::Expression::Try(&#expr) + } +} + +// --------------------------------------------------------------------------- +// Struct / Tuple +// --------------------------------------------------------------------------- + +fn parse_struct(e: &ExprStruct, generics: &[Generic]) -> TokenStream2 { + let path = parse_path(&e.path); + let fields = e.fields.iter().map(|f| parse_struct_field(f, generics)); + quote! { + ::agdb::api_def::Expression::Struct { + name: &#path, + fields: &[#(#fields),*], + } + } +} + +fn parse_struct_field(field: &FieldValue, generics: &[Generic]) -> TokenStream2 { + let field_name = match &field.member { + Member::Named(ident) => ident, + Member::Unnamed(_) => panic!("Unnamed fields are not supported in struct expressions"), + }; + let field_value = parse_expression(&field.expr, generics); + quote! { + (stringify!(#field_name), #field_value) + } +} + +fn parse_tuple(e: &syn::ExprTuple, generics: &[Generic]) -> TokenStream2 { + let elements = e.elems.iter().map(|elem| parse_expression(elem, generics)); + quote! { + ::agdb::api_def::Expression::Tuple(&[#(#elements),*]) + } +} + +// --------------------------------------------------------------------------- +// Patterns (used in let, for, match, closure, etc.) +// --------------------------------------------------------------------------- + +/// Returns `(name_tokens, type_tokens)` where type_tokens is `None` or +/// `Some(fn_ptr)`. +fn parse_pattern(pat: &Pat, generics: &[Generic]) -> (TokenStream2, TokenStream2) { + match pat { + Pat::Ident(p) => { + let name = &p.ident; + ( + quote! { ::agdb::api_def::Expression::Ident(stringify!(#name)) }, + quote! { None }, + ) + } + Pat::Lit(p) => { + let lit = parse_literal(&p.lit); + (lit, quote! { None }) + } + Pat::Type(p) => { + let (name, _) = parse_pattern(&p.pat, generics); + let ty = generics_parser::parse_type(&p.ty, generics); + (name, quote! { Some(#ty) }) + } + Pat::Or(p) => { + let conds: Vec = p + .cases + .iter() + .map(|subpat| parse_pattern(subpat, generics).0) + .collect(); + let mut iter = conds.into_iter(); + let first = iter.next().expect("Or pattern must have cases"); + ( + iter.fold(first, |acc, next| { + quote! { + ::agdb::api_def::Expression::Binary { + op: ::agdb::api_def::Op::Or, + left: &#acc, + right: &#next, + } + } + }), + quote! { None }, + ) + } + Pat::Paren(p) => parse_pattern(&p.pat, generics), + Pat::Path(p) => { + let path = parse_path(&p.path); + (path, quote! { None }) + } + Pat::Reference(p) => parse_pattern(&p.pat, generics), + Pat::Slice(p) => { + let elems = p.elems.iter().map(|elem| parse_pattern(elem, generics).0); + ( + quote! { ::agdb::api_def::Expression::Array(&[#(#elems),*]) }, + quote! { None }, + ) + } + Pat::Struct(p) => { + let path = parse_path(&p.path); + let fields = p.fields.iter().map(|f| parse_pattern(&f.pat, generics).0); + ( + quote! { + ::agdb::api_def::Expression::StructPattern { + name: &#path, + fields: &[#(#fields),*], + } + }, + quote! { None }, + ) + } + Pat::Tuple(p) => { + let elems = p.elems.iter().map(|elem| parse_pattern(elem, generics).0); + ( + quote! { ::agdb::api_def::Expression::Tuple(&[#(#elems),*]) }, + quote! { None }, + ) + } + Pat::TupleStruct(p) => { + let path = parse_path(&p.path); + let elems = p.elems.iter().map(|elem| parse_pattern(elem, generics).0); + ( + quote! { + ::agdb::api_def::Expression::TupleStruct { + name: &#path, + expressions: &[#(#elems),*], + } + }, + quote! { None }, + ) + } + Pat::Wild(_) => (quote! { ::agdb::api_def::Expression::Wild }, quote! { None }), + _ => panic!("Unsupported pattern: {}", pat.to_token_stream()), + } +} diff --git a/agdb_derive/src/api_def/function_def.rs b/agdb_derive/src/api_def/function_def.rs deleted file mode 100644 index 3ff143a4..00000000 --- a/agdb_derive/src/api_def/function_def.rs +++ /dev/null @@ -1,83 +0,0 @@ -use super::generics; -use super::statement; -use super::statement::ExpressionContext; -use crate::api_def::type_def; -use proc_macro2::TokenStream; -use quote::quote; -use syn::FnArg; -use syn::Generics; -use syn::Ident; -use syn::ImplItemFn; -use syn::Token; -use syn::punctuated::Punctuated; - -pub(crate) fn parse_function(input: &ImplItemFn, impl_generics: &Generics) -> TokenStream { - let name = &input.sig.ident; - let mut list_generics = generics::list_generics(impl_generics); - list_generics.extend(generics::list_generics(&input.sig.generics)); - // Treat `Self` as a special generic-like token to avoid referencing it - // inside generated const contexts where `Self` is not permitted. - list_generics.push("Self".to_string()); - let generics = generics::parse_generics(name, &input.sig.generics); - let args = parse_args(name, &input.sig.inputs, &list_generics); - let ret = parse_ret(&input.sig.output, &list_generics); - let async_fn = input.sig.asyncness.is_some(); - let expressions = statement::parse_statements( - &input.block.stmts, - ExpressionContext::new(&name.to_string(), &list_generics), - ); - - quote! { - ::agdb::api_def::Function { - name: stringify!(#name), - generics: &[#(#generics),*], - args: &[#(#args),*], - ret: #ret, - async_fn: #async_fn, - expressions: &[#(#expressions),*], - } - } -} - -pub(crate) fn parse_ret(output: &syn::ReturnType, generics: &[String]) -> TokenStream { - match output { - syn::ReturnType::Default => quote! { None }, - syn::ReturnType::Type(_, ty) => { - let ty_token = type_def::parse_type(ty, generics); - quote! { Some(#ty_token) } - } - } -} - -fn parse_args( - name: &Ident, - inputs: &Punctuated, - generics: &[String], -) -> Vec { - let mut args = vec![]; - - for input in inputs.iter() { - if let FnArg::Typed(pat_type) = input - && let syn::Pat::Ident(pat_ident) = &*pat_type.pat - { - let name = &pat_ident.ident; - let ty = type_def::parse_type(&pat_type.ty, generics); - - args.push(quote! { - ::agdb::api_def::NamedType { - name: stringify!(#name), - ty: Some(#ty), - } - }); - } else if let FnArg::Receiver(_) = input { - continue; - } else { - panic!( - "{name}: Unsupported argument type in function definition: {:?}", - input - ); - } - } - - args -} diff --git a/agdb_derive/src/api_def/generics.rs b/agdb_derive/src/api_def/generics.rs deleted file mode 100644 index 151707a2..00000000 --- a/agdb_derive/src/api_def/generics.rs +++ /dev/null @@ -1,109 +0,0 @@ -use proc_macro2::TokenStream; -use quote::ToTokens; -use quote::quote; -use std::collections::HashMap; -use syn::GenericParam; -use syn::Generics; -use syn::Ident; -use syn::Token; -use syn::TypeParamBound; -use syn::punctuated::Punctuated; - -pub fn parse_bounds( - name: &Ident, - bounds: &Punctuated, -) -> Vec { - bounds - .iter() - .filter_map(|st| match st { - TypeParamBound::Trait(trait_bound) => { - let trait_name = &trait_bound - .path - .segments - .last() - .unwrap_or_else(|| panic!("{name}: Expected trait segment")) - .ident; - - Some(quote! { stringify!(#trait_name) }) - } - TypeParamBound::Lifetime(_) => None, - TypeParamBound::PreciseCapture(_) => { - panic!("{name}: PreciseCapture not supported") - } - TypeParamBound::Verbatim(_) => panic!("{name}: Verbatim not supported"), - _ => None, - }) - .collect() -} - -pub fn parse_generics(name: &Ident, generics: &Generics) -> Vec { - let where_map = if let Some(where_clause) = &generics.where_clause { - parse_where_predicates(name, &where_clause.predicates) - } else { - HashMap::new() - }; - - parse_generic_params(name, &generics.params, where_map) -} - -pub fn list_generics(generics: &Generics) -> Vec { - generics - .params - .iter() - .filter_map(|param| match param { - syn::GenericParam::Type(type_param) => Some(type_param.ident.to_string()), - _ => None, - }) - .collect() -} - -fn parse_where_predicates( - name: &Ident, - predicates: &Punctuated, -) -> HashMap> { - let mut map = HashMap::new(); - predicates.iter().for_each(|pred| match pred { - syn::WherePredicate::Type(type_pred) => { - let type_name = &type_pred.bounded_ty; - let bounds = parse_bounds(name, &type_pred.bounds); - let name_str = type_name.to_token_stream().to_string(); - map.insert(name_str, bounds); - } - syn::WherePredicate::Lifetime(_) => {} - _ => {} - }); - map -} - -fn parse_generic_params( - name: &Ident, - generics: &Punctuated, - where_map: HashMap>, -) -> Vec { - generics - .iter() - .filter_map(|param| match param { - syn::GenericParam::Lifetime(_) => None, - syn::GenericParam::Type(type_param) => { - let type_name = &type_param.ident; - let type_name_str = type_name.to_token_stream().to_string(); - let bounds = if let Some(where_bounds) = where_map.get(&type_name_str) { - where_bounds - } else { - &parse_bounds(name, &type_param.bounds) - }; - - Some(quote! { - ::agdb::api_def::Generic { - name: stringify!(#type_name), - bounds: &[#(#bounds),*], - } - }) - } - syn::GenericParam::Const(const_param) => panic!( - "{name}: Const generic parameters are not supported: {}", - const_param.ident - ), - }) - .collect() -} diff --git a/agdb_derive/src/api_def/statement.rs b/agdb_derive/src/api_def/statement.rs deleted file mode 100644 index 956def9e..00000000 --- a/agdb_derive/src/api_def/statement.rs +++ /dev/null @@ -1,76 +0,0 @@ -use super::expression; -use super::expression::pattern; -use proc_macro2::TokenStream; -use syn::Stmt; -use syn::token::Semi; - -#[derive(Clone, Copy)] -pub(crate) struct ExpressionContext<'a> { - pub fn_name: &'a str, - pub generics: &'a [String], - pub level: usize, - pub semicolon: bool, - pub last: bool, -} - -impl ExpressionContext<'_> { - pub(crate) fn new<'a>(fn_name: &'a str, generics: &'a [String]) -> ExpressionContext<'a> { - ExpressionContext { - fn_name, - generics, - level: 0, - semicolon: false, - last: false, - } - } - - pub(crate) fn inner(&self) -> ExpressionContext<'_> { - ExpressionContext { - fn_name: self.fn_name, - generics: self.generics, - level: self.level + 1, - semicolon: false, - last: false, - } - } - - pub(crate) fn last(&self) -> ExpressionContext<'_> { - ExpressionContext { - fn_name: self.fn_name, - generics: self.generics, - level: self.level, - semicolon: self.semicolon, - last: true, - } - } - - pub(crate) fn semicolon(&self, semi: &Option) -> ExpressionContext<'_> { - ExpressionContext { - fn_name: self.fn_name, - generics: self.generics, - level: self.level, - semicolon: semi.is_some(), - last: self.last, - } - } -} - -pub(crate) fn parse_statements(stmts: &[Stmt], context: ExpressionContext) -> Vec { - stmts - .iter() - .map(|stmt| parse_statement(stmt, context)) - .collect() -} - -pub(crate) fn parse_statement(stmt: &Stmt, context: ExpressionContext) -> TokenStream { - match stmt { - Stmt::Local(local) => pattern::parse_let( - &local.pat, - local.init.as_ref().map(|i| i.expr.as_ref()), - context, - ), - Stmt::Item(item) => panic!("nested items not supported: {item:?}"), - Stmt::Expr(expr, semi) => expression::parse_expression(expr, context.semicolon(semi)), - Stmt::Macro(stmt_macro) => panic!("stmt_macro not supported: {stmt_macro:?}"), - } -} diff --git a/agdb_derive/src/api_def/struct_def.rs b/agdb_derive/src/api_def/struct_def.rs deleted file mode 100644 index 3c3aa2a5..00000000 --- a/agdb_derive/src/api_def/struct_def.rs +++ /dev/null @@ -1,56 +0,0 @@ -use super::generics; -use super::type_def; -use proc_macro2::TokenStream; -use quote::quote; -use syn::DeriveInput; -use syn::FieldsNamed; -use syn::Ident; - -pub(crate) fn parse_struct(fields: Option<&FieldsNamed>, input: &DeriveInput) -> TokenStream { - let name = &input.ident; - let generic_names = generics::list_generics(&input.generics); - let generics = generics::parse_generics(name, &input.generics); - let fields = parse_named_fields(name, fields, &generic_names); - let (impl_generics, ty_generic, where_clause) = input.generics.split_for_impl(); - - quote! { - impl #impl_generics ::agdb::api_def::TypeDefinition for #name #ty_generic #where_clause { - fn type_def() -> ::agdb::api_def::Type { - ::agdb::api_def::Type::Struct(::agdb::api_def::struct_def::Struct { - name: stringify!(#name), - generics: &[#(#generics),*], - fields: &[#(#fields),*], - functions: <#name #ty_generic as ::agdb::api_def::ImplDefinition>::functions() - }) - } - } - } -} - -pub(crate) fn parse_named_fields( - name: &Ident, - fields: Option<&FieldsNamed>, - generics: &[String], -) -> Vec { - if let Some(fields) = fields { - fields - .named - .iter() - .map(|f| { - let field_name = f - .ident - .as_ref() - .unwrap_or_else(|| panic!("{name}: Named fields should have an ident")); - let field_type = type_def::parse_type(&f.ty, generics); - quote! { - ::agdb::api_def::NamedType { - name: stringify!(#field_name), - ty: Some(#field_type), - } - } - }) - .collect() - } else { - Vec::new() - } -} diff --git a/agdb_derive/src/api_def/tuple_def.rs b/agdb_derive/src/api_def/tuple_def.rs deleted file mode 100644 index 74966018..00000000 --- a/agdb_derive/src/api_def/tuple_def.rs +++ /dev/null @@ -1,48 +0,0 @@ -use super::generics; -use super::type_def; -use proc_macro2::TokenStream; -use quote::quote; -use syn::DeriveInput; -use syn::FieldsUnnamed; - -pub(crate) fn parse_tuple(fields: Option<&FieldsUnnamed>, input: &DeriveInput) -> TokenStream { - let name = &input.ident; - let generic_names = generics::list_generics(&input.generics); - let generics = generics::parse_generics(name, &input.generics); - let fields = parse_unnamed_fields(fields, &generic_names); - let (impl_generics, ty_generic, where_clause) = input.generics.split_for_impl(); - - quote! { - impl #impl_generics ::agdb::api_def::TypeDefinition for #name #ty_generic #where_clause { - fn type_def() -> ::agdb::api_def::Type { - ::agdb::api_def::Type::Tuple(::agdb::api_def::tuple_def::Tuple { - name: stringify!(#name), - generics: &[#(#generics),*], - fields: &[#(#fields),*], - functions: <#name #ty_generic as ::agdb::api_def::ImplDefinition>::functions(), - }) - } - } - } -} - -pub(crate) fn parse_unnamed_fields( - fields: Option<&FieldsUnnamed>, - generics: &[String], -) -> Vec { - if let Some(fields) = fields { - fields - .unnamed - .iter() - .map(|f| { - let field_type = type_def::parse_type(&f.ty, generics); - - quote! { - #field_type - } - }) - .collect() - } else { - Vec::new() - } -} diff --git a/agdb_derive/src/api_def/type_def.rs b/agdb_derive/src/api_def/type_def.rs deleted file mode 100644 index d48a95fd..00000000 --- a/agdb_derive/src/api_def/type_def.rs +++ /dev/null @@ -1,70 +0,0 @@ -use super::enum_def; -use super::struct_def; -use super::tuple_def; -use proc_macro2::TokenStream; -use quote::quote; -use syn::DeriveInput; -use syn::Fields; -use syn::GenericArgument; -use syn::PathArguments; -use syn::Type; - -fn type_contains_generic(ty: &Type, generics: &[String]) -> bool { - match ty { - syn::Type::Path(type_path) => { - if let Some(ident_str) = type_path.path.segments.last().map(|s| s.ident.to_string()) - && generics.contains(&ident_str) - { - return true; - } - - for seg in &type_path.path.segments { - if let PathArguments::AngleBracketed(ab) = &seg.arguments { - for arg in &ab.args { - if let GenericArgument::Type(inner_ty) = arg - && type_contains_generic(inner_ty, generics) - { - return true; - } - } - } - } - - false - } - syn::Type::Reference(tr) => type_contains_generic(&tr.elem, generics), - syn::Type::Slice(ts) => type_contains_generic(&ts.elem, generics), - syn::Type::Array(ta) => type_contains_generic(&ta.elem, generics), - syn::Type::Tuple(tt) => tt.elems.iter().any(|e| type_contains_generic(e, generics)), - syn::Type::Paren(tp) => type_contains_generic(&tp.elem, generics), - syn::Type::Group(tg) => type_contains_generic(&tg.elem, generics), - _ => false, - } -} - -pub(crate) fn type_def(input: DeriveInput) -> TokenStream { - match &input.data { - syn::Data::Struct(s) => match &s.fields { - Fields::Named(fields) => struct_def::parse_struct(Some(fields), &input), - Fields::Unnamed(fields) => tuple_def::parse_tuple(Some(fields), &input), - Fields::Unit => struct_def::parse_struct(None, &input), - }, - syn::Data::Enum(e) => enum_def::parse_enum(e, &input), - syn::Data::Union(_) => { - panic!("{}: Union types are not supported", input.ident); - } - } -} - -pub(crate) fn parse_type(ty: &Type, list_generics: &[String]) -> TokenStream { - if type_contains_generic(ty, list_generics) { - quote! { || ::agdb::api_def::Type::Struct(::agdb::api_def::Struct { - name: stringify!(#ty), - generics: &[], - fields: &[], - functions: &[], - }) } - } else { - quote! { <#ty as ::agdb::api_def::TypeDefinition>::type_def } - } -} diff --git a/agdb_derive/src/lib.rs b/agdb_derive/src/lib.rs index 2869d250..616dc121 100644 --- a/agdb_derive/src/lib.rs +++ b/agdb_derive/src/lib.rs @@ -1,7 +1,7 @@ -mod api_def; mod db_serialize; mod db_type; mod db_value; +mod type_def_parser; use proc_macro::TokenStream; @@ -133,15 +133,25 @@ pub fn user_db_value_derive(item: TokenStream) -> TokenStream { #[proc_macro_derive(TypeDef)] pub fn type_def(item: TokenStream) -> TokenStream { - api_def::type_def_impl(item) + type_def_parser::type_def_impl(item) } #[proc_macro_derive(TypeDefImpl)] pub fn type_def_impl(item: TokenStream) -> TokenStream { - api_def::type_def_impl_impl(item) + type_def_parser::type_def_impl_impl(item) } #[proc_macro_attribute] pub fn impl_def(_attr: TokenStream, item: TokenStream) -> TokenStream { - api_def::impl_def_impl(item) + type_def_parser::impl_def_impl(item) +} + +#[proc_macro_attribute] +pub fn trait_def(_attr: TokenStream, item: TokenStream) -> TokenStream { + type_def_parser::trait_def_impl(item) +} + +#[proc_macro_attribute] +pub fn fn_def(_attr: TokenStream, item: TokenStream) -> TokenStream { + type_def_parser::fn_def_impl(item) } diff --git a/agdb_derive/src/type_def_parser.rs b/agdb_derive/src/type_def_parser.rs new file mode 100644 index 00000000..81919289 --- /dev/null +++ b/agdb_derive/src/type_def_parser.rs @@ -0,0 +1,97 @@ +pub(crate) mod enum_parser; +pub(crate) mod expression_parser; +pub(crate) mod function_parser; +pub(crate) mod generics_parser; +pub(crate) mod impl_parser; +pub(crate) mod struct_parser; +pub(crate) mod trait_parser; + +use proc_macro::TokenStream; +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +use syn::DeriveInput; +use syn::Ident; + +pub(crate) fn type_def_impl(item: TokenStream) -> TokenStream { + let input = syn::parse_macro_input!(item as DeriveInput); + + match &input.data { + syn::Data::Struct(s) => struct_parser::parse_struct(&input, s), + syn::Data::Enum(e) => enum_parser::parse_enum(&input, e), + _ => unimplemented!("Only structs and enums are supported for now"), + } + .into() +} + +pub(crate) fn type_def_impl_impl(item: TokenStream) -> TokenStream { + let it = item.clone(); + let def: TokenStream2 = type_def_impl(item).into(); + + let input = syn::parse_macro_input!(it as DeriveInput); + let name = input.ident; + let (impl_generics, ty_generic, where_clause) = input.generics.split_for_impl(); + + quote! { + #def + + impl #impl_generics ::agdb::type_def::ImplDefinition for #name #ty_generic #where_clause {} + } + .into() +} + +pub(crate) fn impl_def_impl(item: TokenStream) -> TokenStream { + let it: TokenStream2 = item.clone().into(); + let def_impl = if let Ok(input) = syn::parse::(item) { + impl_parser::parse_impl(&input) + } else { + unimplemented!("Only impl blocks are supported") + }; + + quote! { + #it + + #def_impl + } + .into() +} + +pub(crate) fn trait_def_impl(item: TokenStream) -> TokenStream { + let it: TokenStream2 = item.clone().into(); + let def_fn = if let Ok(input) = syn::parse::(item) { + trait_parser::parse_trait(&input) + } else { + unimplemented!("Only traits are supported") + }; + + quote! { + #it + + #def_fn + } + .into() +} + +pub(crate) fn fn_def_impl(item: TokenStream) -> TokenStream { + let it: TokenStream2 = item.clone().into(); + let def_fn = if let Ok(input) = syn::parse::(item) { + function_parser::parse_function(&input) + } else { + unimplemented!("Only functions are supported") + }; + + quote! { + #it + + #def_fn + } + .into() +} + +pub(crate) fn type_def_fn(name: &String) -> TokenStream2 { + let bound_fn_name = Ident::new( + &format!("__{name}_type_def"), + proc_macro2::Span::call_site(), + ); + + quote! { #bound_fn_name } +} diff --git a/agdb_derive/src/type_def_parser/enum_parser.rs b/agdb_derive/src/type_def_parser/enum_parser.rs new file mode 100644 index 00000000..ef3751d6 --- /dev/null +++ b/agdb_derive/src/type_def_parser/enum_parser.rs @@ -0,0 +1,103 @@ +use crate::type_def_parser::generics_parser; +use crate::type_def_parser::generics_parser::Generic; +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +use syn::DataEnum; +use syn::DeriveInput; +use syn::Fields; +use syn::FieldsNamed; + +pub(crate) fn parse_enum(input: &DeriveInput, e: &DataEnum) -> TokenStream2 { + let name = &input.ident; + let current_generics = generics_parser::extract_generics(&input.generics); + let generics = generics_parser::parse_generics(&input.generics); + let variants = parse_variants(&e.variants, ¤t_generics); + let (impl_generics, ty_generic, where_clause) = input.generics.split_for_impl(); + + quote! { + impl #impl_generics ::agdb::type_def::TypeDefinition for #name #ty_generic #where_clause { + fn type_def() -> ::agdb::type_def::Type { + ::agdb::type_def::Type::Enum(::agdb::type_def::Enum { + name: stringify!(#name), + generics: &[#(#generics),*], + variants: &[#(#variants),*], + functions: <#name #ty_generic as ::agdb::type_def::ImplDefinition>::functions(), + }) + } + } + } +} + +fn parse_variants( + variants: &syn::punctuated::Punctuated, + generics: &[Generic], +) -> Vec { + variants + .iter() + .map(|v| { + let name = &v.ident; + let ty = parse_variant_fields(&v.fields, generics); + quote! { + ::agdb::type_def::Variable { + name: stringify!(#name), + ty: Some(#ty), + } + } + }) + .collect::>() +} + +fn parse_variant_fields(fields: &Fields, generics: &[Generic]) -> TokenStream2 { + match fields { + Fields::Named(fields_named) => parse_named_fields(fields_named, generics), + Fields::Unnamed(fields_unnamed) => parse_unnamed_fields(fields_unnamed, generics), + Fields::Unit => { + quote! { + <() as ::agdb::type_def::TypeDefinition>::type_def + } + } + } +} + +fn parse_unnamed_fields(fields: &syn::FieldsUnnamed, generics: &[Generic]) -> TokenStream2 { + if fields.unnamed.len() == 1 { + let ty = fields.unnamed.first().map(|f| &f.ty).unwrap(); + generics_parser::parse_type(ty, generics) + } else { + let field_types = fields + .unnamed + .iter() + .map(|f| generics_parser::parse_type(&f.ty, generics)) + .collect::>(); + + quote! { + || ::agdb::type_def::Type::Tuple(&[#(#field_types),*]) + } + } +} + +fn parse_named_fields(fields: &FieldsNamed, generics: &[Generic]) -> TokenStream2 { + let fields = fields + .named + .iter() + .map(|f| { + let name = f.ident.as_ref().unwrap(); + let ty_def = generics_parser::parse_type(&f.ty, generics); + quote! { + ::agdb::type_def::Variable { + name: stringify!(#name), + ty: Some(#ty_def), + } + } + }) + .collect::>(); + + quote! { + || ::agdb::type_def::Type::Struct(::agdb::type_def::Struct { + name: "", + generics: &[], + fields: &[#(#fields),*], + functions: &[], + }) + } +} diff --git a/agdb_derive/src/type_def_parser/expression_parser.rs b/agdb_derive/src/type_def_parser/expression_parser.rs new file mode 100644 index 00000000..f71c9cf2 --- /dev/null +++ b/agdb_derive/src/type_def_parser/expression_parser.rs @@ -0,0 +1,1026 @@ +use crate::type_def_parser::generics_parser; +use crate::type_def_parser::generics_parser::Generic; +use proc_macro2::TokenStream as TokenStream2; +use quote::ToTokens; +use quote::quote; +use syn::BinOp; +use syn::Block; +use syn::Expr; +use syn::ExprArray; +use syn::ExprBinary; +use syn::ExprCall; +use syn::ExprClosure; +use syn::ExprField; +use syn::ExprIndex; +use syn::ExprMacro; +use syn::ExprMethodCall; +use syn::ExprReference; +use syn::ExprStruct; +use syn::ExprTry; +use syn::ExprUnary; +use syn::FieldValue; +use syn::GenericArgument; +use syn::Lit; +use syn::Member; +use syn::Pat; +use syn::PatOr; +use syn::Path; +use syn::PathArguments; +use syn::PathSegment; +use syn::ReturnType; +use syn::Stmt; + +// --------------------------------------------------------------------------- +// Public entry points +// --------------------------------------------------------------------------- + +/// Parse a block into a list of expression token-streams (one per statement). +pub(crate) fn parse_block_stmts(block: &Block, generics: &[Generic]) -> Vec { + block + .stmts + .iter() + .enumerate() + .map(|(i, stmt)| { + let last = i + 1 == block.stmts.len(); + parse_statement(stmt, generics, last) + }) + .collect() +} + +/// Parse a block into an `::agdb::type_def::Expression::Block(...)`. +pub(crate) fn parse_block(block: &Block, generics: &[Generic]) -> TokenStream2 { + let expressions = parse_block_stmts(block, generics); + quote! { + ::agdb::type_def::Expression::Block(&[#(#expressions),*]) + } +} + +/// Parse a single `syn::Expr` into a `TokenStream2` that constructs an +/// `::agdb::type_def::Expression`. +pub(crate) fn parse_expression(e: &Expr, generics: &[Generic]) -> TokenStream2 { + match e { + Expr::Array(e) => parse_array(e, generics), + Expr::Assign(e) => parse_assign(e, generics), + Expr::Async(e) => parse_block(&e.block, generics), + Expr::Await(e) => parse_await(e, generics), + Expr::Binary(e) => parse_binary(e, generics), + Expr::Block(e) => parse_block(&e.block, generics), + Expr::Break(_) => quote! { ::agdb::type_def::Expression::Break }, + Expr::Call(e) => parse_call(e, generics), + Expr::Cast(e) => parse_expression(&e.expr, generics), + Expr::Closure(e) => parse_closure(e, generics), + Expr::Const(e) => parse_block(&e.block, generics), + Expr::Continue(_) => quote! { ::agdb::type_def::Expression::Continue }, + Expr::Field(e) => parse_field_access(e, generics), + Expr::ForLoop(e) => parse_for_loop(e, generics), + Expr::Group(e) => parse_expression(&e.expr, generics), + Expr::If(e) => parse_if(e, generics), + Expr::Index(e) => parse_index(e, generics), + Expr::Infer(_) => quote! { ::agdb::type_def::Expression::Wild }, + Expr::Let(e) => parse_let_expr(e, generics), + Expr::Lit(e) => parse_literal(&e.lit), + Expr::Loop(e) => parse_loop(e, generics), + Expr::Macro(e) => parse_macro(e, generics), + Expr::Match(e) => parse_match(e, generics), + Expr::MethodCall(e) => parse_method_call(e, generics), + Expr::Paren(e) => parse_expression(&e.expr, generics), + Expr::Path(e) => parse_path(&e.path), + Expr::Reference(e) => parse_reference(e, generics), + Expr::Return(e) => parse_return(e, generics), + Expr::Struct(e) => parse_struct(e, generics), + Expr::Try(e) => parse_try(e, generics), + Expr::Tuple(e) => parse_tuple(e, generics), + Expr::Unary(e) => parse_unary(e, generics), + Expr::While(e) => parse_while(e, generics), + _ => panic!("Unsupported expression: {}", e.to_token_stream()), + } +} + +// --------------------------------------------------------------------------- +// Statements +// --------------------------------------------------------------------------- + +fn parse_statement(stmt: &Stmt, generics: &[Generic], last: bool) -> TokenStream2 { + match stmt { + Stmt::Local(local) => parse_local(local, generics), + Stmt::Expr(expr, semi) => { + let parsed = parse_expression(expr, generics); + if last && semi.is_none() && is_returnable(expr) { + quote! { ::agdb::type_def::Expression::Return(Some(&#parsed)) } + } else { + parsed + } + } + Stmt::Item(_) => quote! { ::agdb::type_def::Expression::Block(&[]) }, + Stmt::Macro(m) => parse_stmt_macro(m, generics), + } +} + +fn parse_local(local: &syn::Local, generics: &[Generic]) -> TokenStream2 { + let (name, ty) = parse_pattern(&local.pat, generics); + let value = if let Some(init) = &local.init { + let expr = parse_expression(&init.expr, generics); + quote! { Some(&#expr) } + } else { + quote! { None } + }; + + quote! { + ::agdb::type_def::Expression::Let { + name: &#name, + ty: #ty, + value: #value, + } + } +} + +fn parse_stmt_macro(m: &syn::StmtMacro, generics: &[Generic]) -> TokenStream2 { + let name = path_to_string(&m.mac.path); + parse_macro_by_name(&name, &m.mac.tokens, generics) +} + +fn is_returnable(e: &Expr) -> bool { + !matches!( + e, + Expr::Return(_) + | Expr::Break(_) + | Expr::Continue(_) + | Expr::ForLoop(_) + | Expr::While(_) + | Expr::Loop(_) + | Expr::Match(_) + | Expr::If(_) + ) +} + +// --------------------------------------------------------------------------- +// Array / Index +// --------------------------------------------------------------------------- + +fn parse_array(e: &ExprArray, generics: &[Generic]) -> TokenStream2 { + let elements = e.elems.iter().map(|elem| parse_expression(elem, generics)); + quote! { + ::agdb::type_def::Expression::Array(&[#(#elements),*]) + } +} + +fn parse_index(e: &ExprIndex, generics: &[Generic]) -> TokenStream2 { + let base = parse_expression(&e.expr, generics); + let index = parse_expression(&e.index, generics); + quote! { + ::agdb::type_def::Expression::Index { + base: &#base, + index: &#index, + } + } +} + +// --------------------------------------------------------------------------- +// Assign +// --------------------------------------------------------------------------- + +fn parse_assign(e: &syn::ExprAssign, generics: &[Generic]) -> TokenStream2 { + let target = parse_expression(&e.left, generics); + let value = parse_expression(&e.right, generics); + quote! { + ::agdb::type_def::Expression::Assign { + target: &#target, + value: &#value, + } + } +} + +// --------------------------------------------------------------------------- +// Await +// --------------------------------------------------------------------------- + +fn parse_await(e: &syn::ExprAwait, generics: &[Generic]) -> TokenStream2 { + let expr = parse_expression(&e.base, generics); + quote! { + ::agdb::type_def::Expression::Await(&#expr) + } +} + +// --------------------------------------------------------------------------- +// Binary / Unary / Op +// --------------------------------------------------------------------------- + +fn parse_binary(e: &ExprBinary, generics: &[Generic]) -> TokenStream2 { + let left = parse_expression(&e.left, generics); + let right = parse_expression(&e.right, generics); + let op = parse_binop(&e.op); + quote! { + ::agdb::type_def::Expression::Binary { + op: #op, + left: &#left, + right: &#right, + } + } +} + +fn parse_unary(e: &ExprUnary, generics: &[Generic]) -> TokenStream2 { + let expr = parse_expression(&e.expr, generics); + let op = match &e.op { + syn::UnOp::Deref(_) => quote! { ::agdb::type_def::Op::Deref }, + syn::UnOp::Not(_) => quote! { ::agdb::type_def::Op::Not }, + syn::UnOp::Neg(_) => quote! { ::agdb::type_def::Op::Neg }, + _ => panic!("Unsupported unary operator: {:?}", e.op), + }; + quote! { + ::agdb::type_def::Expression::Unary { + op: #op, + expr: &#expr, + } + } +} + +fn parse_binop(op: &BinOp) -> TokenStream2 { + match op { + BinOp::Add(_) => quote! { ::agdb::type_def::Op::Add }, + BinOp::Sub(_) => quote! { ::agdb::type_def::Op::Sub }, + BinOp::Mul(_) => quote! { ::agdb::type_def::Op::Mul }, + BinOp::Div(_) => quote! { ::agdb::type_def::Op::Div }, + BinOp::Rem(_) => quote! { ::agdb::type_def::Op::Rem }, + BinOp::BitXor(_) => quote! { ::agdb::type_def::Op::BitXor }, + BinOp::BitAnd(_) => quote! { ::agdb::type_def::Op::BitAnd }, + BinOp::BitOr(_) => quote! { ::agdb::type_def::Op::BitOr }, + BinOp::Lt(_) => quote! { ::agdb::type_def::Op::Lt }, + BinOp::Gt(_) => quote! { ::agdb::type_def::Op::Gt }, + BinOp::And(_) => quote! { ::agdb::type_def::Op::And }, + BinOp::Or(_) => quote! { ::agdb::type_def::Op::Or }, + BinOp::Shl(_) => quote! { ::agdb::type_def::Op::Shl }, + BinOp::Shr(_) => quote! { ::agdb::type_def::Op::Shr }, + BinOp::Eq(_) => quote! { ::agdb::type_def::Op::Eq }, + BinOp::Le(_) => quote! { ::agdb::type_def::Op::Le }, + BinOp::Ne(_) => quote! { ::agdb::type_def::Op::Ne }, + BinOp::Ge(_) => quote! { ::agdb::type_def::Op::Ge }, + BinOp::AddAssign(_) => quote! { ::agdb::type_def::Op::AddAssign }, + BinOp::SubAssign(_) => quote! { ::agdb::type_def::Op::SubAssign }, + BinOp::MulAssign(_) => quote! { ::agdb::type_def::Op::MulAssign }, + BinOp::DivAssign(_) => quote! { ::agdb::type_def::Op::DivAssign }, + BinOp::RemAssign(_) => quote! { ::agdb::type_def::Op::RemAssign }, + BinOp::BitXorAssign(_) => quote! { ::agdb::type_def::Op::BitXorAssign }, + BinOp::BitAndAssign(_) => quote! { ::agdb::type_def::Op::BitAndAssign }, + BinOp::BitOrAssign(_) => quote! { ::agdb::type_def::Op::BitOrAssign }, + BinOp::ShlAssign(_) => quote! { ::agdb::type_def::Op::ShlAssign }, + BinOp::ShrAssign(_) => quote! { ::agdb::type_def::Op::ShrAssign }, + _ => panic!("Unsupported binary operator"), + } +} + +// --------------------------------------------------------------------------- +// Call / MethodCall +// --------------------------------------------------------------------------- + +fn parse_call(e: &ExprCall, generics: &[Generic]) -> TokenStream2 { + let function = parse_expression(&e.func, generics); + let args = e.args.iter().map(|arg| parse_expression(arg, generics)); + quote! { + ::agdb::type_def::Expression::Call { + recipient: None, + function: &#function, + args: &[#(#args),*], + } + } +} + +fn parse_method_call(e: &ExprMethodCall, generics: &[Generic]) -> TokenStream2 { + let recipient = parse_expression(&e.receiver, generics); + let method = &e.method; + let turbofish_generics = e + .turbofish + .as_ref() + .map(|gt| { + gt.args + .iter() + .filter_map(|ga| match ga { + GenericArgument::Type(ty) => { + Some(quote! { <#ty as ::agdb::type_def::TypeDefinition>::type_def }) + } + _ => None, + }) + .collect::>() + }) + .unwrap_or_default(); + let args = e.args.iter().map(|arg| parse_expression(arg, generics)); + quote! { + ::agdb::type_def::Expression::Call { + recipient: Some(&#recipient), + function: &::agdb::type_def::Expression::Path { + ident: stringify!(#method), + parent: None, + generics: &[#(#turbofish_generics),*], + }, + args: &[#(#args),*], + } + } +} + +// --------------------------------------------------------------------------- +// Closure +// --------------------------------------------------------------------------- + +fn parse_closure(e: &ExprClosure, generics: &[Generic]) -> TokenStream2 { + let args: Vec = e + .inputs + .iter() + .map(|pat| { + let (name_tokens, ty_tokens) = parse_closure_arg(pat, generics); + quote! { + ::agdb::type_def::Variable { + name: stringify!(#name_tokens), + ty: Some(#ty_tokens), + } + } + }) + .collect(); + let async_fn = e.asyncness.is_some(); + let ret = parse_return_type(&e.output, generics); + let body = match e.body.as_ref() { + Expr::Block(body) => parse_block_stmts(&body.block, generics), + other => { + let expr = parse_expression(other, generics); + vec![quote! { ::agdb::type_def::Expression::Return(Some(&#expr)) }] + } + }; + + quote! { + ::agdb::type_def::Expression::Closure(::agdb::type_def::Function { + name: "", + generics: &[], + args: &[#(#args),*], + ret: #ret, + async_fn: #async_fn, + body: &[#(#body),*], + }) + } +} + +fn parse_closure_arg(pat: &Pat, generics: &[Generic]) -> (TokenStream2, TokenStream2) { + match pat { + Pat::Type(p) => { + let name = extract_pat_ident(&p.pat); + let ty = generics_parser::parse_type(&p.ty, generics); + (quote! { #name }, ty) + } + Pat::Ident(p) => { + let name = &p.ident; + ( + quote! { #name }, + quote! { <() as ::agdb::type_def::TypeDefinition>::type_def }, + ) + } + _ => panic!( + "Unsupported closure argument pattern: {}", + pat.to_token_stream() + ), + } +} + +fn extract_pat_ident(pat: &Pat) -> &syn::Ident { + match pat { + Pat::Ident(p) => &p.ident, + _ => panic!( + "Expected identifier pattern, got: {}", + pat.to_token_stream() + ), + } +} + +fn parse_return_type(ret: &ReturnType, generics: &[Generic]) -> TokenStream2 { + match ret { + ReturnType::Default => quote! { <() as ::agdb::type_def::TypeDefinition>::type_def }, + ReturnType::Type(_, ty) => generics_parser::parse_type(ty, generics), + } +} + +// --------------------------------------------------------------------------- +// Field access / Tuple access +// --------------------------------------------------------------------------- + +fn parse_field_access(e: &ExprField, generics: &[Generic]) -> TokenStream2 { + let base = parse_expression(&e.base, generics); + match &e.member { + Member::Named(ident) => { + quote! { + ::agdb::type_def::Expression::FieldAccess { + base: &#base, + field: stringify!(#ident), + } + } + } + Member::Unnamed(index) => { + let idx = index.index; + quote! { + ::agdb::type_def::Expression::TupleAccess { + base: &#base, + index: #idx, + } + } + } + } +} + +// --------------------------------------------------------------------------- +// Loops +// --------------------------------------------------------------------------- + +fn parse_for_loop(e: &syn::ExprForLoop, generics: &[Generic]) -> TokenStream2 { + let (pattern, _) = parse_pattern(&e.pat, generics); + let iterable = parse_expression(&e.expr, generics); + let body = parse_block(&e.body, generics); + quote! { + ::agdb::type_def::Expression::For { + pattern: &#pattern, + iterable: &#iterable, + body: &#body, + } + } +} + +fn parse_loop(e: &syn::ExprLoop, generics: &[Generic]) -> TokenStream2 { + let body = parse_block(&e.body, generics); + quote! { + ::agdb::type_def::Expression::While { + condition: &::agdb::type_def::Expression::Literal(::agdb::type_def::LiteralValue::Bool(true)), + body: &#body, + } + } +} + +fn parse_while(e: &syn::ExprWhile, generics: &[Generic]) -> TokenStream2 { + let condition = parse_expression(&e.cond, generics); + let body = parse_block(&e.body, generics); + quote! { + ::agdb::type_def::Expression::While { + condition: &#condition, + body: &#body, + } + } +} + +// --------------------------------------------------------------------------- +// If / Match +// --------------------------------------------------------------------------- + +fn parse_if(e: &syn::ExprIf, generics: &[Generic]) -> TokenStream2 { + let condition = parse_expression(&e.cond, generics); + let then_branch = parse_block(&e.then_branch, generics); + + let else_branch = if let Some((_, else_expr)) = &e.else_branch { + let else_tokens = match else_expr.as_ref() { + Expr::If(else_if) => parse_if(else_if, generics), + Expr::Block(else_block) => parse_block(&else_block.block, generics), + _ => panic!("Unsupported else branch"), + }; + quote! { Some(&#else_tokens) } + } else { + quote! { None } + }; + + quote! { + ::agdb::type_def::Expression::If { + condition: &#condition, + then_branch: &#then_branch, + else_branch: #else_branch, + } + } +} + +fn parse_match(e: &syn::ExprMatch, generics: &[Generic]) -> TokenStream2 { + let subject = parse_expression(&e.expr, generics); + let mut branches = Vec::new(); + let mut else_branch: Option = None; + + for arm in &e.arms { + if matches!(&arm.pat, Pat::Wild(_)) { + else_branch = Some(parse_match_arm_body(&arm.body, generics)); + } else { + let condition = parse_match_condition(&subject, &arm.pat, generics); + let condition_with_guard = if let Some((_, guard)) = &arm.guard { + let guard_expr = parse_expression(guard, generics); + quote! { + ::agdb::type_def::Expression::Binary { + op: ::agdb::type_def::Op::And, + left: &#condition, + right: &#guard_expr, + } + } + } else { + condition + }; + let body = parse_match_arm_body(&arm.body, generics); + branches.push((condition_with_guard, body)); + } + } + + if branches.is_empty() { + else_branch.expect("Match expression must have at least one arm") + } else { + branches + .iter() + .rev() + .fold(else_branch, |else_br, (cond, body)| { + let else_part = if let Some(eb) = else_br { + eb + } else { + quote! { ::agdb::type_def::Expression::Block(&[]) } + }; + Some(quote! { + ::agdb::type_def::Expression::If { + condition: &#cond, + then_branch: &#body, + else_branch: Some(&#else_part), + } + }) + }) + .expect("At least one match arm present") + } +} + +fn parse_match_arm_body(body: &Expr, generics: &[Generic]) -> TokenStream2 { + match body { + Expr::Block(b) => parse_block(&b.block, generics), + Expr::Tuple(t) if t.elems.is_empty() => { + quote! { ::agdb::type_def::Expression::Block(&[]) } + } + expr => { + let inner = parse_expression(expr, generics); + quote! { ::agdb::type_def::Expression::Block(&[#inner]) } + } + } +} + +fn parse_match_condition(subject: &TokenStream2, pat: &Pat, generics: &[Generic]) -> TokenStream2 { + if let Pat::Or(p) = pat { + return parse_match_or(subject, p, generics); + } + + let (rhs, _) = parse_pattern(pat, generics); + quote! { + ::agdb::type_def::Expression::Binary { + op: ::agdb::type_def::Op::Eq, + left: &#subject, + right: &#rhs, + } + } +} + +fn parse_match_or(subject: &TokenStream2, pat_or: &PatOr, generics: &[Generic]) -> TokenStream2 { + let conds: Vec = pat_or + .cases + .iter() + .map(|subpat| parse_match_condition(subject, subpat, generics)) + .collect(); + let mut iter = conds.into_iter(); + let first = iter.next().expect("Or pattern must have cases"); + iter.fold(first, |acc, next| { + quote! { + ::agdb::type_def::Expression::Binary { + op: ::agdb::type_def::Op::Or, + left: &#acc, + right: &#next, + } + } + }) +} + +// --------------------------------------------------------------------------- +// Let (expression form, e.g. `if let`) +// --------------------------------------------------------------------------- + +fn parse_let_expr(e: &syn::ExprLet, generics: &[Generic]) -> TokenStream2 { + let (name, ty) = parse_pattern(&e.pat, generics); + let value = parse_expression(&e.expr, generics); + quote! { + ::agdb::type_def::Expression::Let { + name: &#name, + ty: #ty, + value: Some(&#value), + } + } +} + +// --------------------------------------------------------------------------- +// Literal +// --------------------------------------------------------------------------- + +fn parse_literal(lit: &Lit) -> TokenStream2 { + match lit { + Lit::Str(s) => { + let value = s.value(); + quote! { + ::agdb::type_def::Expression::Literal(::agdb::type_def::LiteralValue::Str(#value)) + } + } + Lit::Int(i) => { + let suffix = i.suffix(); + match suffix { + "i8" => { + let v = i.base10_parse::().unwrap(); + quote! { ::agdb::type_def::Expression::Literal(::agdb::type_def::LiteralValue::I8(#v)) } + } + "i16" => { + let v = i.base10_parse::().unwrap(); + quote! { ::agdb::type_def::Expression::Literal(::agdb::type_def::LiteralValue::I16(#v)) } + } + "i32" | "" => { + let v = i.base10_parse::().unwrap(); + quote! { ::agdb::type_def::Expression::Literal(::agdb::type_def::LiteralValue::I32(#v)) } + } + "u8" => { + let v = i.base10_parse::().unwrap(); + quote! { ::agdb::type_def::Expression::Literal(::agdb::type_def::LiteralValue::U8(#v)) } + } + "u16" => { + let v = i.base10_parse::().unwrap(); + quote! { ::agdb::type_def::Expression::Literal(::agdb::type_def::LiteralValue::U16(#v)) } + } + "u32" => { + let v = i.base10_parse::().unwrap(); + quote! { ::agdb::type_def::Expression::Literal(::agdb::type_def::LiteralValue::U32(#v)) } + } + "u64" => { + let v = i.base10_parse::().unwrap(); + quote! { ::agdb::type_def::Expression::Literal(::agdb::type_def::LiteralValue::U64(#v)) } + } + "usize" => { + let v = i.base10_parse::().unwrap(); + quote! { ::agdb::type_def::Expression::Literal(::agdb::type_def::LiteralValue::Usize(#v)) } + } + _ => panic!("Unsupported integer suffix: {suffix}"), + } + } + Lit::Float(f) => { + let suffix = f.suffix(); + match suffix { + "f32" => { + let v = f.base10_parse::().unwrap(); + quote! { ::agdb::type_def::Expression::Literal(::agdb::type_def::LiteralValue::F32(#v)) } + } + "f64" | "" => { + let v = f.base10_parse::().unwrap(); + quote! { ::agdb::type_def::Expression::Literal(::agdb::type_def::LiteralValue::F64(#v)) } + } + _ => panic!("Unsupported float suffix: {suffix}"), + } + } + Lit::Bool(b) => { + let value = b.value; + quote! { + ::agdb::type_def::Expression::Literal(::agdb::type_def::LiteralValue::Bool(#value)) + } + } + Lit::Char(c) => { + let value = c.value().to_string(); + quote! { + ::agdb::type_def::Expression::Literal(::agdb::type_def::LiteralValue::Str(#value)) + } + } + _ => panic!("Unsupported literal: {:?}", lit), + } +} + +// --------------------------------------------------------------------------- +// Macro (format!, vec!, etc.) +// --------------------------------------------------------------------------- + +fn parse_macro(e: &ExprMacro, generics: &[Generic]) -> TokenStream2 { + let name = path_to_string(&e.mac.path); + parse_macro_by_name(&name, &e.mac.tokens, generics) +} + +fn parse_macro_by_name( + name: &str, + tokens: &proc_macro2::TokenStream, + generics: &[Generic], +) -> TokenStream2 { + let args: syn::punctuated::Punctuated = syn::parse::Parser::parse2( + syn::punctuated::Punctuated::parse_terminated, + tokens.clone(), + ) + .unwrap_or_default(); + + match name { + "vec" => { + let elements = args.iter().map(|arg| parse_expression(arg, generics)); + quote! { + ::agdb::type_def::Expression::Array(&[#(#elements),*]) + } + } + "format" => { + let mut args_iter = args.into_iter(); + let format_string_expr = args_iter + .next() + .expect("format! requires at least one argument"); + let format_string = extract_format_string(&format_string_expr); + let (fmt_str, fmt_args) = + extract_format_parts(&format_string, &mut args_iter, generics); + quote! { + ::agdb::type_def::Expression::Format { + format_string: #fmt_str, + args: &[#(#fmt_args),*], + } + } + } + // Common macros treated as calls with string arguments + "panic" | "todo" | "unimplemented" | "println" | "eprintln" | "dbg" | "assert" + | "assert_eq" | "assert_ne" | "debug_assert" | "debug_assert_eq" | "debug_assert_ne" + | "unreachable" | "write" | "writeln" => { + let macro_args = args.iter().map(|arg| parse_expression(arg, generics)); + quote! { + ::agdb::type_def::Expression::Call { + recipient: None, + function: &::agdb::type_def::Expression::Path { + ident: #name, + parent: None, + generics: &[], + }, + args: &[#(#macro_args),*], + } + } + } + _ => panic!("Unsupported macro: {name}"), + } +} + +fn extract_format_string(e: &Expr) -> String { + match e { + Expr::Lit(expr_lit) => { + if let Lit::Str(lit_str) = &expr_lit.lit { + lit_str.value() + } else { + panic!("First argument to format! must be a string literal"); + } + } + _ => panic!("First argument to format! must be a string literal"), + } +} + +fn extract_format_parts( + format_string: &str, + args_iter: &mut impl Iterator, + generics: &[Generic], +) -> (TokenStream2, Vec) { + let mut args = Vec::new(); + let mut fmt_str = String::new(); + let mut chars = format_string.chars(); + + while let Some(c) = chars.next() { + fmt_str.push(c); + + if c == '{' + && let Some(next) = chars.next() + { + if next == '{' { + fmt_str.push(next); + continue; // escaped brace + } + + fmt_str.push('}'); + + if next == '}' { + // positional argument + let arg = args_iter + .next() + .expect("not enough arguments for format string"); + args.push(parse_expression(&arg, generics)); + } else { + // named argument + let mut ident = next.to_string(); + for nc in chars.by_ref() { + if nc == '}' { + break; + } + ident.push(nc); + } + args.push(quote! { ::agdb::type_def::Expression::Ident(#ident) }); + } + } + } + + (quote! { #fmt_str }, args) +} + +// --------------------------------------------------------------------------- +// Path +// --------------------------------------------------------------------------- + +fn parse_path(path: &Path) -> TokenStream2 { + let mut iter = path.segments.iter(); + let first = iter.next().expect("path should have at least one segment"); + + // Single-segment path with no generics => Ident + if path.segments.len() == 1 && matches!(first.arguments, PathArguments::None) { + let ident = &first.ident; + return quote! { + ::agdb::type_def::Expression::Ident(stringify!(#ident)) + }; + } + + let first_segment = parse_path_segment(first, quote! { None }); + iter.fold(first_segment, |parent, segment| { + parse_path_segment(segment, quote! { Some(&#parent) }) + }) +} + +fn parse_path_segment(segment: &PathSegment, parent: TokenStream2) -> TokenStream2 { + let ident = &segment.ident; + let generics = match &segment.arguments { + PathArguments::AngleBracketed(args) => args + .args + .iter() + .filter_map(|ga| match ga { + GenericArgument::Type(ty) => { + Some(quote! { <#ty as ::agdb::type_def::TypeDefinition>::type_def }) + } + _ => None, + }) + .collect::>(), + PathArguments::Parenthesized(args) => args + .inputs + .iter() + .map(|ty| quote! { <#ty as ::agdb::type_def::TypeDefinition>::type_def }) + .collect::>(), + PathArguments::None => Vec::new(), + }; + + quote! { + ::agdb::type_def::Expression::Path { + ident: stringify!(#ident), + parent: #parent, + generics: &[#(#generics),*], + } + } +} + +fn path_to_string(path: &Path) -> String { + path.segments + .last() + .expect("path should not be empty") + .ident + .to_string() +} + +// --------------------------------------------------------------------------- +// Reference / Return / Try +// --------------------------------------------------------------------------- + +fn parse_reference(e: &ExprReference, generics: &[Generic]) -> TokenStream2 { + let expr = parse_expression(&e.expr, generics); + quote! { + ::agdb::type_def::Expression::Reference(&#expr) + } +} + +fn parse_return(e: &syn::ExprReturn, generics: &[Generic]) -> TokenStream2 { + if let Some(expr) = &e.expr { + let parsed = parse_expression(expr, generics); + quote! { + ::agdb::type_def::Expression::Return(Some(&#parsed)) + } + } else { + quote! { + ::agdb::type_def::Expression::Return(None) + } + } +} + +fn parse_try(e: &ExprTry, generics: &[Generic]) -> TokenStream2 { + let expr = parse_expression(&e.expr, generics); + quote! { + ::agdb::type_def::Expression::Try(&#expr) + } +} + +// --------------------------------------------------------------------------- +// Struct / Tuple +// --------------------------------------------------------------------------- + +fn parse_struct(e: &ExprStruct, generics: &[Generic]) -> TokenStream2 { + let path = parse_path(&e.path); + let fields = e.fields.iter().map(|f| parse_struct_field(f, generics)); + quote! { + ::agdb::type_def::Expression::Struct { + name: &#path, + fields: &[#(#fields),*], + } + } +} + +fn parse_struct_field(field: &FieldValue, generics: &[Generic]) -> TokenStream2 { + let field_name = match &field.member { + Member::Named(ident) => ident, + Member::Unnamed(_) => panic!("Unnamed fields are not supported in struct expressions"), + }; + let field_value = parse_expression(&field.expr, generics); + quote! { + (stringify!(#field_name), #field_value) + } +} + +fn parse_tuple(e: &syn::ExprTuple, generics: &[Generic]) -> TokenStream2 { + let elements = e.elems.iter().map(|elem| parse_expression(elem, generics)); + quote! { + ::agdb::type_def::Expression::Tuple(&[#(#elements),*]) + } +} + +// --------------------------------------------------------------------------- +// Patterns (used in let, for, match, closure, etc.) +// --------------------------------------------------------------------------- + +/// Returns `(name_tokens, type_tokens)` where type_tokens is `None` or +/// `Some(fn_ptr)`. +fn parse_pattern(pat: &Pat, generics: &[Generic]) -> (TokenStream2, TokenStream2) { + match pat { + Pat::Ident(p) => { + let name = &p.ident; + ( + quote! { ::agdb::type_def::Expression::Ident(stringify!(#name)) }, + quote! { None }, + ) + } + Pat::Lit(p) => { + let lit = parse_literal(&p.lit); + (lit, quote! { None }) + } + Pat::Type(p) => { + let (name, _) = parse_pattern(&p.pat, generics); + let ty = generics_parser::parse_type(&p.ty, generics); + (name, quote! { Some(#ty) }) + } + Pat::Or(p) => { + let conds: Vec = p + .cases + .iter() + .map(|subpat| parse_pattern(subpat, generics).0) + .collect(); + let mut iter = conds.into_iter(); + let first = iter.next().expect("Or pattern must have cases"); + ( + iter.fold(first, |acc, next| { + quote! { + ::agdb::type_def::Expression::Binary { + op: ::agdb::type_def::Op::Or, + left: &#acc, + right: &#next, + } + } + }), + quote! { None }, + ) + } + Pat::Paren(p) => parse_pattern(&p.pat, generics), + Pat::Path(p) => { + let path = parse_path(&p.path); + (path, quote! { None }) + } + Pat::Reference(p) => parse_pattern(&p.pat, generics), + Pat::Slice(p) => { + let elems = p.elems.iter().map(|elem| parse_pattern(elem, generics).0); + ( + quote! { ::agdb::type_def::Expression::Array(&[#(#elems),*]) }, + quote! { None }, + ) + } + Pat::Struct(p) => { + let path = parse_path(&p.path); + let fields = p.fields.iter().map(|f| parse_pattern(&f.pat, generics).0); + ( + quote! { + ::agdb::type_def::Expression::StructPattern { + name: &#path, + fields: &[#(#fields),*], + } + }, + quote! { None }, + ) + } + Pat::Tuple(p) => { + let elems = p.elems.iter().map(|elem| parse_pattern(elem, generics).0); + ( + quote! { ::agdb::type_def::Expression::Tuple(&[#(#elems),*]) }, + quote! { None }, + ) + } + Pat::TupleStruct(p) => { + let path = parse_path(&p.path); + let elems = p.elems.iter().map(|elem| parse_pattern(elem, generics).0); + ( + quote! { + ::agdb::type_def::Expression::TupleStruct { + name: &#path, + expressions: &[#(#elems),*], + } + }, + quote! { None }, + ) + } + Pat::Wild(_) => ( + quote! { ::agdb::type_def::Expression::Wild }, + quote! { None }, + ), + _ => panic!("Unsupported pattern: {}", pat.to_token_stream()), + } +} diff --git a/agdb_derive/src/type_def_parser/function_parser.rs b/agdb_derive/src/type_def_parser/function_parser.rs new file mode 100644 index 00000000..45c59fc0 --- /dev/null +++ b/agdb_derive/src/type_def_parser/function_parser.rs @@ -0,0 +1,160 @@ +use crate::type_def_parser::generics_parser; +use crate::type_def_parser::generics_parser::Generic; +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +use syn::FnArg; +use syn::ItemFn; +use syn::Pat; +use syn::ReturnType; +use syn::punctuated::Punctuated; +use syn::token::Comma; + +pub(crate) fn parse_function(input: &ItemFn) -> TokenStream2 { + let name_str = input.sig.ident.to_string(); + let fn_name = crate::type_def_parser::type_def_fn(&name_str); + let current_generics = generics_parser::extract_generics(&input.sig.generics); + let generics = generics_parser::parse_generics(&input.sig.generics); + let args = parse_args(&input.sig.inputs, ¤t_generics); + let ret = parse_ret(&input.sig.output, ¤t_generics); + let async_fn = input.sig.asyncness.is_some(); + let body = crate::type_def_parser::expression_parser::parse_block_stmts( + &input.block, + ¤t_generics, + ); + let lt_params = generics_parser::parse_lifetime_params(&input.sig.generics); + let lt_generics = if lt_params.is_empty() { + quote! {} + } else { + quote! { <#(#lt_params),*> } + }; + let name = &input.sig.ident; + + quote! { + fn #fn_name #lt_generics () -> ::agdb::type_def::Type { + ::agdb::type_def::Type::Function(::agdb::type_def::Function { + name: stringify!(#name), + generics: &[#(#generics),*], + args: &[#(#args),*], + ret: #ret, + async_fn: #async_fn, + body: &[#(#body),*], + }) + } + } +} + +pub(crate) fn parse_signature(sig: &syn::Signature) -> TokenStream2 { + let name = &sig.ident; + let current_generics = generics_parser::extract_generics(&sig.generics); + let generics = generics_parser::parse_generics(&sig.generics); + let args = parse_args(&sig.inputs, ¤t_generics); + let ret = parse_ret(&sig.output, ¤t_generics); + let async_fn = sig.asyncness.is_some(); + + quote! { + ::agdb::type_def::Function { + name: stringify!(#name), + generics: &[#(#generics),*], + args: &[#(#args),*], + ret: #ret, + async_fn: #async_fn, + body: &[], + } + } +} + +pub(crate) fn parse_trait_fn( + sig: &syn::Signature, + default_block: Option<&syn::Block>, + current_generics: &[Generic], +) -> TokenStream2 { + let name = &sig.ident; + let generics = generics_parser::parse_generics(&sig.generics); + let args = parse_args(&sig.inputs, current_generics); + let ret = parse_ret(&sig.output, current_generics); + let async_fn = sig.asyncness.is_some(); + let body = default_block + .map(|block| { + crate::type_def_parser::expression_parser::parse_block_stmts(block, current_generics) + }) + .unwrap_or_default(); + + quote! { + ::agdb::type_def::Function { + name: stringify!(#name), + generics: &[#(#generics),*], + args: &[#(#args),*], + ret: #ret, + async_fn: #async_fn, + body: &[#(#body),*], + } + } +} + +fn parse_args(args: &Punctuated, generics: &[Generic]) -> Vec { + args.iter() + .map(|arg| match arg { + FnArg::Typed(pat_ty) => { + let name = match pat_ty.pat.as_ref() { + Pat::Ident(pat_ident) => { + let ident = &pat_ident.ident; + quote! { stringify!(#ident) } + } + _ => quote! { "" }, + }; + let ty_def = generics_parser::parse_type(&pat_ty.ty, generics); + + quote! { + ::agdb::type_def::Variable { + name: #name, + ty: Some(#ty_def), + } + } + } + FnArg::Receiver(rec) => { + if let Some(_token) = rec.colon_token { + let ty = &rec.ty; + quote! { + ::agdb::type_def::Variable { + name: "self", + ty: Some(<#ty as ::agdb::type_def::TypeDefinition>::type_def), + } + } + } else { + let mutable = rec.mutability.is_some(); + + let ty = if let Some((_, lt_opt)) = &rec.reference { + let lifetime = if let Some(lt) = lt_opt { + let lt_str = lt.ident.to_string(); + quote! { Some(#lt_str) } + } else { + quote! { None } + }; + quote! { + || ::agdb::type_def::Type::Reference(::agdb::type_def::Reference { + mutable: #mutable, + lifetime: #lifetime, + ty: || ::agdb::type_def::Type::SelfType(#mutable), + }) + } + } else { + quote! { || ::agdb::type_def::Type::SelfType(#mutable) } + }; + quote! { + ::agdb::type_def::Variable { + name: "self", + ty: Some(#ty), + } + } + } + } + }) + .collect() +} + +fn parse_ret(ret: &ReturnType, generics: &[Generic]) -> TokenStream2 { + match ret { + ReturnType::Default => quote! { <() as ::agdb::type_def::TypeDefinition>::type_def }, + ReturnType::Type(_, ty) => generics_parser::parse_type(ty, generics), + } +} diff --git a/agdb_derive/src/type_def_parser/generics_parser.rs b/agdb_derive/src/type_def_parser/generics_parser.rs new file mode 100644 index 00000000..7a9d7c6b --- /dev/null +++ b/agdb_derive/src/type_def_parser/generics_parser.rs @@ -0,0 +1,269 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::ToTokens; +use quote::quote; +use syn::GenericParam; +use syn::Generics; +use syn::TypeParamBound; + +#[derive(Clone)] +pub(crate) struct Generic { + pub name: String, + pub bounds: Vec, +} + +pub(crate) fn extract_generics(generics: &Generics) -> Vec { + let mut extracted: Vec = Vec::new(); + + if let Some(where_clause) = &generics.where_clause { + where_clause.predicates.iter().for_each(|predicate| { + if let syn::WherePredicate::Type(pred) = predicate { + extracted.push(Generic { + name: pred.bounded_ty.to_token_stream().to_string(), + bounds: pred.bounds.iter().map(parse_type_param_bound).collect(), + }); + } + }); + } + + generics.params.iter().for_each(|param| { + if let GenericParam::Type(ty) = param { + let name = ty.ident.to_string(); + if !extracted.iter().any(|g| g.name == name) { + extracted.push(Generic { + name, + bounds: ty.bounds.iter().map(parse_type_param_bound).collect(), + }); + } + } + }); + + extracted +} + +pub(crate) fn parse_generics(generics: &Generics) -> Vec { + let extracted = extract_generics(generics); + + generics + .params + .iter() + .map(|param| match param { + GenericParam::Lifetime(lt) => { + let name = lt.lifetime.ident.to_string(); + quote! { + ::agdb::type_def::Generic { + kind: ::agdb::type_def::GenericKind::Lifetime, + name: #name, + bounds: &[], + } + } + } + GenericParam::Type(ty) => { + let name = ty.ident.to_string(); + let bounds = extracted + .iter() + .find(|g| g.name == name) + .map(|g| g.bounds.clone()) + .unwrap_or_default(); + quote! { + ::agdb::type_def::Generic { + kind: ::agdb::type_def::GenericKind::Type, + name: #name, + bounds: &[#(#bounds),*], + } + } + } + GenericParam::Const(const_param) => { + let name = const_param.ident.to_string(); + let ty = &const_param.ty; + quote! { + ::agdb::type_def::Generic { + kind: ::agdb::type_def::GenericKind::Const, + name: #name, + bounds: &[<#ty as ::agdb::type_def::TypeDefinition>::type_def], + } + } + } + }) + .collect() +} + +pub(crate) fn parse_type_param_bound(bound: &TypeParamBound) -> TokenStream2 { + let name = extract_type_param_bound(bound); + + quote! { + || ::agdb::type_def::Type::Trait(::agdb::type_def::Trait { + name: #name, + generics: &[], + bounds: &[], + functions: &[], + }) + } +} + +fn extract_type_param_bound(bound: &TypeParamBound) -> String { + match bound { + TypeParamBound::Trait(trait_bound) => { + trait_bound.path.segments.last().unwrap().ident.to_string() + } + _ => unimplemented!("Only trait bounds are supported for now"), + } +} + +pub(crate) fn parse_lifetime_params(generics: &Generics) -> Vec<&syn::LifetimeParam> { + generics + .params + .iter() + .filter_map(|param| { + if let GenericParam::Lifetime(lt) = param { + Some(lt) + } else { + None + } + }) + .collect() +} + +pub(crate) fn parse_type(ty: &syn::Type, generics: &[Generic]) -> TokenStream2 { + let ty_str = quote! { #ty }.to_string(); + + if let Some(generic) = generics.iter().find(|g| g.name == ty_str) { + let bounds = &generic.bounds; + quote! { + || ::agdb::type_def::Type::Generic(::agdb::type_def::Generic { + kind: ::agdb::type_def::GenericKind::Type, + name: stringify!(#ty), + bounds: &[#(#bounds),*], + }) + } + } else if let syn::Type::Tuple(tuple) = ty { + let fields = tuple + .elems + .iter() + .map(|elem| parse_type(elem, generics)) + .collect::>(); + quote! { + || ::agdb::type_def::Type::Tuple(&[#(#fields),*]) + } + } else if let syn::Type::Path(type_path) = ty { + if type_path.qself.is_none() + && let Some(last) = type_path.path.segments.last() + { + let ident = last.ident.to_string(); + + if ident == "Option" + && let syn::PathArguments::AngleBracketed(args) = &last.arguments + && let Some(syn::GenericArgument::Type(inner_ty)) = args.args.first() + { + let inner = parse_type(inner_ty, generics); + return quote! { + || ::agdb::type_def::Type::Option(#inner) + }; + } + + if ident == "Vec" + && let syn::PathArguments::AngleBracketed(args) = &last.arguments + && let Some(syn::GenericArgument::Type(inner_ty)) = args.args.first() + { + let inner = parse_type(inner_ty, generics); + return quote! { + || ::agdb::type_def::Type::Vec(#inner) + }; + } + + if ident == "Result" + && let syn::PathArguments::AngleBracketed(args) = &last.arguments + { + let mut type_args = args.args.iter().filter_map(|arg| { + if let syn::GenericArgument::Type(ty) = arg { + Some(ty) + } else { + None + } + }); + + if let (Some(ok_ty), Some(err_ty)) = (type_args.next(), type_args.next()) { + let ok = parse_type(ok_ty, generics); + let err = parse_type(err_ty, generics); + return quote! { + || ::agdb::type_def::Type::Result { + ok: #ok, + err: #err, + } + }; + } + } + } + + if type_contains_generic(ty, generics) { + quote! { + || ::agdb::type_def::Type::Generic(::agdb::type_def::Generic { + kind: ::agdb::type_def::GenericKind::Type, + name: stringify!(#ty), + bounds: &[], + }) + } + } else { + quote! { <#ty as ::agdb::type_def::TypeDefinition>::type_def } + } + } else if let syn::Type::Reference(type_ref) = ty { + let mutable = type_ref.mutability.is_some(); + let lifetime = if let Some(lt) = &type_ref.lifetime { + let lt_str = lt.ident.to_string(); + quote! { Some(#lt_str) } + } else { + quote! { None } + }; + let inner = match type_ref.elem.as_ref() { + syn::Type::Path(p) if p.path.is_ident("str") => { + quote! { || ::agdb::type_def::Type::Literal(::agdb::type_def::Literal::Str) } + } + syn::Type::Slice(slice) => { + let elem = parse_type(&slice.elem, generics); + quote! { || ::agdb::type_def::Type::Slice(#elem) } + } + other => parse_type(other, generics), + }; + quote! { + || ::agdb::type_def::Type::Reference(::agdb::type_def::Reference { + mutable: #mutable, + lifetime: #lifetime, + ty: #inner, + }) + } + } else { + quote! { <#ty as ::agdb::type_def::TypeDefinition>::type_def } + } +} + +fn type_contains_generic(ty: &syn::Type, generics: &[Generic]) -> bool { + match ty { + syn::Type::Path(type_path) => { + if let Some(ident_str) = type_path.path.segments.last().map(|s| s.ident.to_string()) + && generics.iter().any(|g| g.name == ident_str) + { + return true; + } + + for seg in &type_path.path.segments { + if let syn::PathArguments::AngleBracketed(ab) = &seg.arguments { + for arg in &ab.args { + if let syn::GenericArgument::Type(inner_ty) = arg + && type_contains_generic(inner_ty, generics) + { + return true; + } + } + } + } + + false + } + syn::Type::Reference(tr) => type_contains_generic(&tr.elem, generics), + syn::Type::Slice(ts) => type_contains_generic(&ts.elem, generics), + syn::Type::Array(ta) => type_contains_generic(&ta.elem, generics), + syn::Type::Tuple(tt) => tt.elems.iter().any(|e| type_contains_generic(e, generics)), + syn::Type::Paren(tp) => type_contains_generic(&tp.elem, generics), + syn::Type::Group(tg) => type_contains_generic(&tg.elem, generics), + _ => false, + } +} diff --git a/agdb_derive/src/type_def_parser/impl_parser.rs b/agdb_derive/src/type_def_parser/impl_parser.rs new file mode 100644 index 00000000..c801071a --- /dev/null +++ b/agdb_derive/src/type_def_parser/impl_parser.rs @@ -0,0 +1,48 @@ +use crate::type_def_parser::function_parser; +use crate::type_def_parser::generics_parser; +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +use syn::ImplItem; + +pub(crate) fn parse_impl(input: &syn::ItemImpl) -> TokenStream2 { + let ty = &input.self_ty; + let generics = generics_parser::parse_generics(&input.generics); + let (impl_generics, _, where_clause) = input.generics.split_for_impl(); + + let trait_part = if let Some((_, path, _)) = &input.trait_ { + let trait_name = path.segments.last().unwrap().ident.to_string(); + quote! { + Some(|| ::agdb::type_def::Type::Trait(::agdb::type_def::Trait { + name: #trait_name, + generics: &[], + bounds: &[], + functions: &[], + })) + } + } else { + quote! { None } + }; + + let methods = input + .items + .iter() + .map(|item| match item { + ImplItem::Fn(impl_fn) => function_parser::parse_signature(&impl_fn.sig), + _ => panic!("Only function items are supported in impl blocks"), + }) + .collect::>(); + + quote! { + impl #impl_generics ::agdb::type_def::ImplDefinition for #ty #where_clause { + fn impl_def() -> ::agdb::type_def::Impl { + ::agdb::type_def::Impl { + name: stringify!(#ty), + generics: &[#(#generics),*], + trait_: #trait_part, + ty: <#ty as ::agdb::type_def::TypeDefinition>::type_def, + functions: &[#(#methods),*], + } + } + } + } +} diff --git a/agdb_derive/src/type_def_parser/struct_parser.rs b/agdb_derive/src/type_def_parser/struct_parser.rs new file mode 100644 index 00000000..f7a6c8f7 --- /dev/null +++ b/agdb_derive/src/type_def_parser/struct_parser.rs @@ -0,0 +1,48 @@ +use crate::type_def_parser::generics_parser; +use crate::type_def_parser::generics_parser::Generic; +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +use syn::DataStruct; +use syn::DeriveInput; +use syn::Fields; + +pub(crate) fn parse_struct(input: &DeriveInput, s: &DataStruct) -> TokenStream2 { + let name = &input.ident; + let current_generics = generics_parser::extract_generics(&input.generics); + let generics = generics_parser::parse_generics(&input.generics); + let fields = parse_fields(&s.fields, ¤t_generics); + let (impl_generics, ty_generic, where_clause) = input.generics.split_for_impl(); + + quote! { + impl #impl_generics ::agdb::type_def::TypeDefinition for #name #ty_generic #where_clause { + fn type_def() -> ::agdb::type_def::Type { + ::agdb::type_def::Type::Struct(::agdb::type_def::Struct { + name: stringify!(#name), + generics: &[#(#generics),*], + fields: &[#(#fields),*], + functions: <#name #ty_generic as ::agdb::type_def::ImplDefinition>::functions(), + }) + } + } + } +} + +fn parse_fields(fields: &Fields, generics: &[Generic]) -> Vec { + fields + .iter() + .map(|f| { + let name = f + .ident + .as_ref() + .map(|ident| quote! { stringify!(#ident) }) + .unwrap_or_else(|| quote! { "" }); + let ty_def = generics_parser::parse_type(&f.ty, generics); + quote! { + ::agdb::type_def::Variable { + name: #name, + ty: Some(#ty_def), + } + } + }) + .collect::>() +} diff --git a/agdb_derive/src/type_def_parser/trait_parser.rs b/agdb_derive/src/type_def_parser/trait_parser.rs new file mode 100644 index 00000000..48426d9f --- /dev/null +++ b/agdb_derive/src/type_def_parser/trait_parser.rs @@ -0,0 +1,48 @@ +use crate::type_def_parser::function_parser; +use crate::type_def_parser::generics_parser; +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +use syn::ItemTrait; +use syn::TraitItem; + +pub(crate) fn parse_trait(input: &ItemTrait) -> TokenStream2 { + let name_str = input.ident.to_string(); + let fn_name = crate::type_def_parser::type_def_fn(&name_str); + let lt_params = generics_parser::parse_lifetime_params(&input.generics); + let lt_generics = if lt_params.is_empty() { + quote! {} + } else { + quote! { <#(#lt_params),*> } + }; + let generics = generics_parser::parse_generics(&input.generics); + let bounds = input + .supertraits + .iter() + .map(generics_parser::parse_type_param_bound); + let trait_generics = generics_parser::extract_generics(&input.generics); + let functions = input.items.iter().filter_map(|item| match item { + TraitItem::Fn(trait_fn) => { + let fn_generics = generics_parser::extract_generics(&trait_fn.sig.generics); + let mut combined = Vec::new(); + combined.extend_from_slice(&trait_generics); + combined.extend_from_slice(&fn_generics); + Some(function_parser::parse_trait_fn( + &trait_fn.sig, + trait_fn.default.as_ref(), + &combined, + )) + } + _ => None, + }); + + quote! { + fn #fn_name #lt_generics () -> ::agdb::type_def::Type { + ::agdb::type_def::Type::Trait(::agdb::type_def::Trait { + name: #name_str, + generics: &[#(#generics),*], + bounds: &[#(#bounds),*], + functions: &[#(#functions),*], + }) + } + } +}