use crate::frontend::ast::{
ClassConstant, ClassMethod, ClassProperty, Constructor, Decorator, Expr, ExprKind, Param,
PropertySetter, SelfType, Span, StructField, Visibility,
};
use crate::frontend::lexer::Token;
use crate::frontend::parser::{bail, parse_expr_recursive, utils, ParserState, Result};
pub(in crate::frontend::parser) fn parse_class_definition(
state: &mut ParserState,
name: String,
type_params: Vec<String>,
start_span: Span,
) -> Result<Expr> {
let (superclass, traits) = parse_inheritance(state)?;
let (fields, constructors, methods, constants, properties) = parse_class_body(state)?;
Ok(Expr::new(
ExprKind::Class {
name,
type_params,
superclass,
traits,
fields,
constructors,
methods,
constants,
properties,
derives: Vec::new(),
decorators: Vec::new(),
is_pub: false,
is_sealed: false,
is_abstract: false,
},
start_span,
))
}
fn parse_identifier_with_generics(state: &mut ParserState) -> Result<String> {
let Some((Token::Identifier(name), _)) = state.tokens.peek() else {
bail!("Expected identifier");
};
let mut name = name.clone();
state.tokens.advance();
if matches!(state.tokens.peek(), Some((Token::Less, _))) {
state.tokens.advance(); name.push('<');
loop {
let type_param = utils::parse_type(state)?;
name.push_str(&format!("{type_param:?}"));
if matches!(state.tokens.peek(), Some((Token::Comma, _))) {
state.tokens.advance();
name.push_str(", ");
} else {
break;
}
}
state.tokens.expect(&Token::Greater)?;
name.push('>');
}
Ok(name)
}
fn parse_inheritance(state: &mut ParserState) -> Result<(Option<String>, Vec<String>)> {
if !matches!(state.tokens.peek(), Some((Token::Colon, _))) {
return Ok((None, Vec::new()));
}
state.tokens.advance();
let superclass = if matches!(state.tokens.peek(), Some((Token::Identifier(_), _))) {
Some(parse_identifier_with_generics(state)?)
} else {
None
};
let mut traits = Vec::new();
while matches!(state.tokens.peek(), Some((Token::Plus, _))) {
state.tokens.advance();
if matches!(state.tokens.peek(), Some((Token::Identifier(_), _))) {
traits.push(parse_identifier_with_generics(state)?);
} else {
bail!("Expected trait name after '+'");
}
}
Ok((superclass, traits))
}
fn parse_class_body(
state: &mut ParserState,
) -> Result<(
Vec<StructField>,
Vec<Constructor>,
Vec<ClassMethod>,
Vec<ClassConstant>,
Vec<ClassProperty>,
)> {
state.tokens.expect(&Token::LeftBrace)?;
let mut fields = Vec::new();
let mut constructors = Vec::new();
let mut methods = Vec::new();
let mut constants = Vec::new();
let mut properties = Vec::new();
while !matches!(state.tokens.peek(), Some((Token::RightBrace, _))) {
parse_class_member(
state,
&mut fields,
&mut constructors,
&mut methods,
&mut constants,
&mut properties,
)?;
consume_optional_separator(state);
}
state.tokens.expect(&Token::RightBrace)?;
Ok((fields, constructors, methods, constants, properties))
}
fn parse_class_member(
state: &mut ParserState,
fields: &mut Vec<StructField>,
constructors: &mut Vec<Constructor>,
methods: &mut Vec<ClassMethod>,
constants: &mut Vec<ClassConstant>,
properties: &mut Vec<ClassProperty>,
) -> Result<()> {
let decorators = parse_member_decorators(state)?;
if try_parse_class_constant(state, constants)? {
return Ok(());
}
if try_parse_class_property(state, properties)? {
return Ok(());
}
validate_no_unsupported_features(state)?;
if try_parse_operator_method(state, methods)? {
return Ok(());
}
parse_member_and_dispatch(state, fields, constructors, methods, decorators)
}
fn parse_member_decorators(state: &mut ParserState) -> Result<Vec<Decorator>> {
if is_decorator_start(state) {
parse_decorators(state)
} else {
Ok(Vec::new())
}
}
fn is_decorator_start(state: &mut ParserState) -> bool {
match state.tokens.peek() {
Some((Token::At, _)) => true,
Some((Token::Label(label), _)) => label.starts_with('@'),
_ => false,
}
}
fn try_parse_class_constant(
state: &mut ParserState,
constants: &mut Vec<ClassConstant>,
) -> Result<bool> {
if matches!(state.tokens.peek(), Some((Token::Const, _))) {
state.tokens.advance();
let constant = parse_class_constant(state)?;
constants.push(constant);
Ok(true)
} else {
Ok(false)
}
}
fn try_parse_class_property(
state: &mut ParserState,
properties: &mut Vec<ClassProperty>,
) -> Result<bool> {
if matches!(state.tokens.peek(), Some((Token::Property, _))) {
state.tokens.advance();
let property = parse_class_property(state)?;
properties.push(property);
Ok(true)
} else {
Ok(false)
}
}
fn validate_no_unsupported_features(state: &mut ParserState) -> Result<()> {
if matches!(state.tokens.peek(), Some((Token::Impl, _))) {
bail!("Impl blocks inside classes are not yet supported");
}
if matches!(state.tokens.peek(), Some((Token::Class, _))) {
bail!("Nested classes are not yet supported");
}
Ok(())
}
fn try_parse_operator_method(
state: &mut ParserState,
methods: &mut Vec<ClassMethod>,
) -> Result<bool> {
if matches!(state.tokens.peek(), Some((Token::Operator, _))) {
state.tokens.advance();
let operator_method = parse_operator_method(state)?;
methods.push(operator_method);
Ok(true)
} else {
Ok(false)
}
}
fn parse_member_and_dispatch(
state: &mut ParserState,
fields: &mut Vec<StructField>,
constructors: &mut Vec<Constructor>,
methods: &mut Vec<ClassMethod>,
decorators: Vec<Decorator>,
) -> Result<()> {
let (visibility, is_mut) = parse_class_modifiers(state)?;
let (is_static, is_override, is_final, is_abstract, is_async) = parse_member_flags(state)?;
match state.tokens.peek() {
Some((Token::Identifier(name), _)) if name == "new" || name == "init" => {
parse_and_add_constructor(state, constructors, visibility)
}
Some((Token::Fun | Token::Fn, _)) => parse_and_add_method(
state,
methods,
MethodModifiers {
is_pub: visibility.is_public(),
is_static,
is_override,
is_final,
is_abstract,
is_async,
},
),
Some((Token::Let, _)) => {
state.tokens.advance(); parse_and_add_field(state, fields, visibility, is_mut, decorators)
}
Some((Token::Identifier(_), _)) if !is_static => {
parse_and_add_field(state, fields, visibility, is_mut, decorators)
}
_ => bail!("Expected field, constructor, method, or constant in class body"),
}
}
fn parse_and_add_constructor(
state: &mut ParserState,
constructors: &mut Vec<Constructor>,
visibility: Visibility,
) -> Result<()> {
validate_constructor_modifiers(false, false)?;
let mut constructor = parse_constructor(state)?;
constructor.is_pub = visibility.is_public();
constructors.push(constructor);
Ok(())
}
struct MethodModifiers {
is_pub: bool,
is_static: bool,
is_override: bool,
is_final: bool,
is_abstract: bool,
is_async: bool,
}
fn parse_and_add_method(
state: &mut ParserState,
methods: &mut Vec<ClassMethod>,
modifiers: MethodModifiers,
) -> Result<()> {
let mut method = parse_class_method(state, modifiers.is_abstract)?;
apply_method_modifiers(&mut method, modifiers)?;
methods.push(method);
Ok(())
}
fn parse_and_add_field(
state: &mut ParserState,
fields: &mut Vec<StructField>,
visibility: Visibility,
is_mut: bool,
decorators: Vec<Decorator>,
) -> Result<()> {
let (field_name, field_type, default_value) = super::structs::parse_single_struct_field(state)?;
fields.push(StructField {
name: field_name,
ty: field_type,
visibility,
is_mut,
default_value,
decorators,
});
Ok(())
}
fn parse_operator_method(state: &mut ParserState) -> Result<ClassMethod> {
let operator_name = match state.tokens.peek() {
Some((Token::Plus, _)) => {
state.tokens.advance();
"add"
}
Some((Token::Minus, _)) => {
state.tokens.advance();
"sub"
}
Some((Token::Star, _)) => {
state.tokens.advance();
"mul"
}
Some((Token::Slash, _)) => {
state.tokens.advance();
"div"
}
Some((Token::EqualEqual, _)) => {
state.tokens.advance();
"eq"
}
Some((Token::NotEqual, _)) => {
state.tokens.advance();
"ne"
}
Some((Token::Less, _)) => {
state.tokens.advance();
"lt"
}
Some((Token::Greater, _)) => {
state.tokens.advance();
"gt"
}
Some((Token::LessEqual, _)) => {
state.tokens.advance();
"le"
}
Some((Token::GreaterEqual, _)) => {
state.tokens.advance();
"ge"
}
Some((Token::Percent, _)) => {
state.tokens.advance();
"rem"
}
Some((Token::LeftBracket, _)) => {
state.tokens.advance();
state.tokens.expect(&Token::RightBracket)?;
"index"
}
_ => bail!("Expected operator symbol after 'operator' keyword"),
};
let params = utils::parse_params(state)?;
let return_type = if matches!(state.tokens.peek(), Some((Token::Arrow, _))) {
state.tokens.advance();
Some(utils::parse_type(state)?)
} else {
None
};
let body = if matches!(state.tokens.peek(), Some((Token::LeftBrace, _))) {
Box::new(parse_expr_recursive(state)?)
} else {
bail!("Expected method body after operator signature")
};
Ok(ClassMethod {
name: format!("op_{operator_name}"),
params,
return_type,
body,
is_pub: true,
is_static: false,
is_override: false,
is_final: false,
is_abstract: false,
is_async: false,
self_type: SelfType::Borrowed, })
}
fn parse_decorator_value(state: &mut ParserState) -> Result<String> {
match state.tokens.peek() {
Some((Token::Integer(n), _)) => {
let v = n.clone();
state.tokens.advance();
Ok(v)
}
Some((Token::Float(f), _)) => {
let v = f.to_string();
state.tokens.advance();
Ok(v)
}
Some((Token::String(s), _)) => {
let v = s.clone();
state.tokens.advance();
Ok(v)
}
Some((Token::Identifier(id), _)) if id == "true" || id == "false" => {
let v = id.clone();
state.tokens.advance();
Ok(v)
}
_ => bail!("Expected value after '=' in decorator argument"),
}
}
fn parse_decorator_argument(state: &mut ParserState) -> Result<String> {
match state.tokens.peek() {
Some((Token::String(s), _)) => {
let value = s.clone();
state.tokens.advance();
Ok(value)
}
Some((Token::Identifier(key), _)) => {
let key = key.clone();
state.tokens.advance();
if matches!(state.tokens.peek(), Some((Token::Equal, _))) {
state.tokens.advance(); let value = parse_decorator_value(state)?;
Ok(format!("{key}={value}"))
} else {
Ok(key)
}
}
_ => bail!("Expected argument in decorator"),
}
}
fn parse_decorator_args(state: &mut ParserState) -> Result<Vec<String>> {
if !matches!(state.tokens.peek(), Some((Token::LeftParen, _))) {
return Ok(Vec::new());
}
state.tokens.advance(); let mut args = Vec::new();
while !matches!(state.tokens.peek(), Some((Token::RightParen, _))) {
args.push(parse_decorator_argument(state)?);
if matches!(state.tokens.peek(), Some((Token::Comma, _))) {
state.tokens.advance();
} else if !matches!(state.tokens.peek(), Some((Token::RightParen, _))) {
bail!("Expected ',' or ')' in decorator arguments");
}
}
state.tokens.expect(&Token::RightParen)?;
Ok(args)
}
fn parse_decorator(state: &mut ParserState) -> Result<Decorator> {
let name = match state.tokens.peek() {
Some((Token::Label(label), _)) if label.starts_with('@') => {
let name = label.strip_prefix('@').unwrap_or(label).to_string();
state.tokens.advance();
name
}
Some((Token::At, _)) => {
state.tokens.advance(); match state.tokens.peek() {
Some((Token::Identifier(n), _)) => {
let name = n.clone();
state.tokens.advance();
name
}
_ => bail!("Expected decorator name after '@'"),
}
}
_ => bail!("Expected '@' or decorator label"),
};
let args = parse_decorator_args(state)?;
Ok(Decorator { name, args })
}
pub(in crate::frontend::parser) fn parse_decorators(
state: &mut ParserState,
) -> Result<Vec<Decorator>> {
let mut decorators = Vec::new();
while is_decorator_start(state) {
decorators.push(parse_decorator(state)?);
}
Ok(decorators)
}
fn parse_class_constant(state: &mut ParserState) -> Result<ClassConstant> {
let name = match state.tokens.peek() {
Some((Token::Identifier(n), _)) => {
let name = n.clone();
state.tokens.advance();
name
}
_ => bail!("Expected constant name after 'const'"),
};
state.tokens.expect(&Token::Colon)?;
let ty = utils::parse_type(state)?;
state.tokens.expect(&Token::Equal)?;
let value = parse_expr_recursive(state)?;
Ok(ClassConstant {
name,
ty,
value,
is_pub: true, })
}
fn parse_class_property(state: &mut ParserState) -> Result<ClassProperty> {
let name = parse_property_name(state)?;
state.tokens.expect(&Token::Colon)?;
let ty = utils::parse_type(state)?;
state.tokens.expect(&Token::LeftBrace)?;
let (getter, setter) = parse_property_accessors(state)?;
state.tokens.expect(&Token::RightBrace)?;
Ok(ClassProperty {
name,
ty,
getter,
setter,
is_pub: true,
})
}
fn parse_property_name(state: &mut ParserState) -> Result<String> {
match state.tokens.peek() {
Some((Token::Identifier(n), _)) => {
let name = n.clone();
state.tokens.advance();
Ok(name)
}
_ => bail!("Expected property name after 'property'"),
}
}
fn parse_property_accessors(
state: &mut ParserState,
) -> Result<(Option<Box<Expr>>, Option<PropertySetter>)> {
let mut getter = None;
let mut setter = None;
while !matches!(state.tokens.peek(), Some((Token::RightBrace, _))) {
match state.tokens.peek() {
Some((Token::Identifier(keyword), _)) if keyword == "get" => {
getter = Some(parse_property_getter(state)?);
}
Some((Token::Identifier(keyword), _)) if keyword == "set" => {
setter = Some(parse_property_setter(state)?);
}
_ => bail!("Expected 'get' or 'set' in property body"),
}
if matches!(state.tokens.peek(), Some((Token::Comma, _))) {
state.tokens.advance();
}
}
Ok((getter, setter))
}
fn parse_property_getter(state: &mut ParserState) -> Result<Box<Expr>> {
state.tokens.advance(); state.tokens.expect(&Token::FatArrow)?;
let body = parse_expr_recursive(state)?;
Ok(Box::new(body))
}
fn parse_property_setter(state: &mut ParserState) -> Result<PropertySetter> {
state.tokens.advance(); state.tokens.expect(&Token::LeftParen)?;
let param_name = match state.tokens.peek() {
Some((Token::Identifier(n), _)) => {
let name = n.clone();
state.tokens.advance();
name
}
_ => bail!("Expected parameter name for setter"),
};
state.tokens.expect(&Token::RightParen)?;
state.tokens.expect(&Token::FatArrow)?;
let body = parse_expr_recursive(state)?;
Ok(PropertySetter {
param_name,
body: Box::new(body),
})
}
fn parse_class_modifiers(state: &mut ParserState) -> Result<(Visibility, bool)> {
let mut visibility = try_parse_visibility_modifier(state)?;
let is_mut = try_parse_mut_modifier(state);
if matches!(visibility, Visibility::Private) {
let second_visibility = try_parse_visibility_modifier(state)?;
if !matches!(second_visibility, Visibility::Private) {
visibility = second_visibility;
}
}
Ok((visibility, is_mut))
}
fn try_parse_visibility_modifier(state: &mut ParserState) -> Result<Visibility> {
match state.tokens.peek() {
Some((Token::Private, _)) => {
state.tokens.advance();
Ok(Visibility::Private)
}
Some((Token::Protected, _)) => {
state.tokens.advance();
Ok(Visibility::Protected)
}
Some((Token::Pub, _)) => {
state.tokens.advance();
parse_pub_scope_modifier(state)
}
_ => Ok(Visibility::Private),
}
}
fn parse_pub_scope_modifier(state: &mut ParserState) -> Result<Visibility> {
if !matches!(state.tokens.peek(), Some((Token::LeftParen, _))) {
return Ok(Visibility::Public);
}
state.tokens.advance(); let visibility = match state.tokens.peek() {
Some((Token::Crate, _)) => {
state.tokens.advance();
Visibility::PubCrate
}
Some((Token::Super, _)) => {
state.tokens.advance();
Visibility::PubSuper
}
Some((Token::Identifier(scope), _)) => {
let scope = scope.clone();
state.tokens.advance();
state.tokens.expect(&Token::RightParen)?;
bail!("Unsupported visibility scope: pub({scope}) - only pub(crate) and pub(super) are supported");
}
_ => bail!("Expected 'crate', 'super', or identifier after 'pub('"),
};
state.tokens.expect(&Token::RightParen)?;
Ok(visibility)
}
fn try_parse_mut_modifier(state: &mut ParserState) -> bool {
if matches!(state.tokens.peek(), Some((Token::Mut, _))) {
state.tokens.advance();
true
} else {
false
}
}
fn parse_member_flags(state: &mut ParserState) -> Result<(bool, bool, bool, bool, bool)> {
let is_static = matches!(state.tokens.peek(), Some((Token::Static, _)));
if is_static {
state.tokens.advance();
}
let is_override = matches!(state.tokens.peek(), Some((Token::Override, _)));
if is_override {
state.tokens.advance();
}
let is_final = matches!(state.tokens.peek(), Some((Token::Final, _)));
if is_final {
state.tokens.advance();
}
let is_abstract = matches!(state.tokens.peek(), Some((Token::Abstract, _)));
if is_abstract {
state.tokens.advance();
}
let is_async = matches!(state.tokens.peek(), Some((Token::Async, _)));
if is_async {
state.tokens.advance();
}
Ok((is_static, is_override, is_final, is_abstract, is_async))
}
fn validate_constructor_modifiers(is_static: bool, is_override: bool) -> Result<()> {
if is_static {
bail!("Constructors cannot be static");
}
if is_override {
bail!("Constructors cannot be override");
}
Ok(())
}
fn apply_method_modifiers(method: &mut ClassMethod, modifiers: MethodModifiers) -> Result<()> {
method.is_pub = modifiers.is_pub;
method.is_static = modifiers.is_static;
method.is_override = modifiers.is_override;
method.is_final = modifiers.is_final;
method.is_abstract = modifiers.is_abstract;
method.is_async = modifiers.is_async;
if modifiers.is_static {
method.self_type = SelfType::None;
if modifiers.is_override {
bail!("Static methods cannot be override");
}
}
if modifiers.is_final && modifiers.is_override {
bail!("Methods cannot be both final and override");
}
if modifiers.is_abstract && modifiers.is_final {
bail!("Methods cannot be both abstract and final");
}
if modifiers.is_abstract && modifiers.is_static {
bail!("Static methods cannot be abstract");
}
Ok(())
}
fn consume_optional_separator(state: &mut ParserState) {
if matches!(
state.tokens.peek(),
Some((Token::Comma | Token::Semicolon, _))
) {
state.tokens.advance();
}
}
fn expect_new_keyword(state: &mut ParserState) -> Result<()> {
if let Some((Token::Identifier(name), _)) = state.tokens.peek() {
if name == "new" || name == "init" {
state.tokens.advance();
Ok(())
} else {
bail!("Expected 'new' or 'init' keyword");
}
} else {
bail!("Expected 'new' or 'init' keyword");
}
}
fn parse_optional_constructor_name(state: &mut ParserState) -> Option<String> {
if !matches!(state.tokens.peek(), Some((Token::Identifier(_), _))) {
return None;
}
let saved_pos = state.tokens.position();
if let Some((Token::Identifier(name), _)) = state.tokens.peek() {
let name = name.clone();
state.tokens.advance();
if matches!(state.tokens.peek(), Some((Token::LeftParen, _))) {
Some(name)
} else {
state.tokens.set_position(saved_pos);
None
}
} else {
None
}
}
fn parse_constructor(state: &mut ParserState) -> Result<Constructor> {
expect_new_keyword(state)?;
let constructor_name = parse_optional_constructor_name(state);
let params = utils::parse_params(state)?;
let return_type = if matches!(state.tokens.peek(), Some((Token::Arrow, _))) {
state.tokens.advance();
Some(utils::parse_type(state)?)
} else {
None
};
let body = Box::new(parse_expr_recursive(state)?);
Ok(Constructor {
name: constructor_name,
params,
return_type,
body,
is_pub: false, })
}
fn parse_class_method(state: &mut ParserState, is_abstract: bool) -> Result<ClassMethod> {
match state.tokens.peek() {
Some((Token::Fun, _)) => {
state.tokens.advance();
}
Some((Token::Fn, _)) => {
state.tokens.advance();
}
_ => bail!("Expected 'fun' or 'fn' keyword for method definition"),
}
let method_name = match state.tokens.peek() {
Some((Token::Identifier(name), _)) => {
let name = name.clone();
state.tokens.advance();
name
}
Some((Token::From, _)) => {
state.tokens.advance();
"from".to_string()
}
Some((Token::Default, _)) => {
state.tokens.advance();
"default".to_string()
}
_ => bail!("Expected method name after 'fn'"),
};
let params = utils::parse_params(state)?;
let self_type = determine_self_type_from_params(¶ms);
let return_type = if matches!(state.tokens.peek(), Some((Token::Arrow, _))) {
state.tokens.advance();
Some(utils::parse_type(state)?)
} else {
None
};
let body = if is_abstract {
Box::new(Expr::new(ExprKind::Block(Vec::new()), Span::default()))
} else {
Box::new(parse_expr_recursive(state)?)
};
Ok(ClassMethod {
name: method_name,
params,
return_type,
body,
is_pub: false, is_static: matches!(self_type, SelfType::None),
is_override: false, is_final: false, is_abstract: false, is_async: false, self_type,
})
}
fn determine_self_type_from_params(params: &[Param]) -> SelfType {
if !params.is_empty() && params[0].name() == "self" {
use crate::frontend::ast::TypeKind;
match ¶ms[0].ty.kind {
TypeKind::Reference { is_mut: true, .. } => SelfType::MutBorrowed,
TypeKind::Reference { is_mut: false, .. } => SelfType::Borrowed,
_ => SelfType::Owned,
}
} else {
SelfType::None }
}
#[cfg(test)]
mod tests {
use crate::frontend::parser::Parser;
#[test]
fn test_basic_class() {
let code = "class MyClass { }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Basic class should parse");
}
#[test]
fn test_class_with_fields() {
let code = "class Point { x: f64 y: f64 }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Class with fields should parse");
}
#[test]
fn test_class_with_inheritance() {
let code = "class Child : Parent { }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Class with inheritance should parse");
}
#[test]
fn test_class_with_traits() {
let code = "class MyClass : ParentClass + Trait1 + Trait2 { }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Class with traits should parse");
}
#[test]
fn test_class_with_constructor() {
let code = "class Point { new(x: f64, y: f64) { self.x = x; self.y = y } }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Class with constructor should parse");
}
#[test]
fn test_class_with_method() {
let code = "class Point { fun distance(&self) -> f64 { 0.0 } }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Class with method should parse");
}
#[test]
fn test_generic_class() {
let code = "class Container<T> { value: T }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Generic class should parse");
}
#[test]
fn test_class_with_init_constructor() {
let code = "class Point { init(x: f64) { self.x = x } }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Class with init constructor should parse");
}
#[test]
fn test_class_with_multiple_constructors() {
let code = "class Point { new() { } new(x: f64) { self.x = x } }";
let result = Parser::new(code).parse();
assert!(
result.is_ok(),
"Class with multiple constructors should parse"
);
}
#[test]
fn test_class_with_static_method() {
let code = "class Math { static fun add(a: i32, b: i32) -> i32 { a + b } }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Class with static method should parse");
}
#[test]
fn test_class_with_pub_field() {
let code = "class Point { pub x: f64 }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Class with pub field should parse");
}
#[test]
fn test_class_with_mut_field() {
let code = "class Counter { mut count: i32 }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Class with mut field should parse");
}
#[test]
fn test_class_with_pub_mut_field() {
let code = "class Counter { pub mut count: i32 }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Class with pub mut field should parse");
}
#[test]
fn test_class_with_const() {
let code = "class Math { const PI: f64 = 3.14159 }";
let result = Parser::new(code).parse();
assert!(result.is_ok() || result.is_err());
}
#[test]
fn test_class_with_typed_const() {
let code = "class Math { const MAX: i32 = 100 }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Class with typed const should parse");
}
#[test]
fn test_class_with_self_method() {
let code = "class Point { fun get_x(&self) -> f64 { self.x } }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Class with &self method should parse");
}
#[test]
fn test_class_with_mut_self_method() {
let code = "class Counter { fun increment(&mut self) { self.count = self.count + 1 } }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Class with &mut self method should parse");
}
#[test]
fn test_class_with_owned_self_method() {
let code = "class Point { fun into_tuple(self) -> (f64, f64) { (self.x, self.y) } }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Class with owned self method should parse");
}
#[test]
fn test_class_with_override_method() {
let code = "class Child : Parent { override fun method(&self) { } }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Class with override method should parse");
}
#[test]
fn test_class_with_final_method() {
let code = "class Base { final fun method(&self) { } }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Class with final method should parse");
}
#[test]
fn test_class_with_abstract_method() {
let code = "class Base { abstract fun method(&self) }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Class with abstract method should parse");
}
#[test]
fn test_class_with_async_method() {
let code = "class AsyncClass { async fun fetch(&self) { } }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Class with async method should parse");
}
#[test]
fn test_class_with_fn_method() {
let code = "class Point { fn get_x(&self) -> f64 { self.x } }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Class with fn keyword should parse");
}
#[test]
fn test_class_with_generic_inheritance() {
let code = "class IntContainer : Container<i32> { }";
let result = Parser::new(code).parse();
assert!(
result.is_ok(),
"Class with generic inheritance should parse"
);
}
#[test]
fn test_class_with_generic_method() {
let code = "class Factory { fun create<T>() -> T { } }";
let result = Parser::new(code).parse();
assert!(result.is_ok() || result.is_err());
}
#[test]
fn test_class_with_let_field() {
let code = "class Point { let x: f64 }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Class with let field should parse");
}
#[test]
fn test_class_traits_only() {
let code = "class MyClass : + Trait1 + Trait2 { }";
let result = Parser::new(code).parse();
assert!(result.is_ok() || result.is_err());
}
#[test]
fn test_class_with_return_type() {
let code = "class Point { fun magnitude(&self) -> f64 { 0.0 } }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Class method with return type should parse");
}
#[test]
fn test_class_with_no_return_type() {
let code = "class Logger { fun log(&self, msg: String) { } }";
let result = Parser::new(code).parse();
assert!(
result.is_ok(),
"Class method without return type should parse"
);
}
#[test]
fn test_class_with_multiple_type_params() {
let code = "class Map<K, V> { }";
let result = Parser::new(code).parse();
assert!(
result.is_ok(),
"Class with multiple type params should parse"
);
}
#[test]
fn test_class_with_field_initialization() {
let code = "class Point { x: f64 = 0.0 }";
let result = Parser::new(code).parse();
assert!(result.is_ok() || result.is_err());
}
#[test]
fn test_class_complete_example() {
let code = r#"
class Vector2D : BaseVector + Comparable + Serializable {
pub mut x: f64
pub mut y: f64
new(x: f64, y: f64) {
self.x = x
self.y = y
}
fun magnitude(&self) -> f64 {
0.0
}
static fun zero() -> Vector2D {
Vector2D::new(0.0, 0.0)
}
}
"#;
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Complete class example should parse");
}
#[test]
fn test_class_empty_body() {
let code = "class Empty { }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Empty class body should parse");
}
#[test]
fn test_class_field_separator_comma() {
let code = "class Point { x: f64, y: f64 }";
let result = Parser::new(code).parse();
assert!(
result.is_ok(),
"Class with comma-separated fields should parse"
);
}
#[test]
fn test_class_field_separator_semicolon() {
let code = "class Point { x: f64; y: f64 }";
let result = Parser::new(code).parse();
assert!(
result.is_ok(),
"Class with semicolon-separated fields should parse"
);
}
#[test]
fn test_class_field_separator_newline() {
let code = "class Point {\n x: f64\n y: f64\n}";
let result = Parser::new(code).parse();
assert!(
result.is_ok(),
"Class with newline-separated fields should parse"
);
}
#[test]
fn test_class_with_pub_constructor() {
let code = "class Point { pub new() { } }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Class with pub constructor should parse");
}
#[test]
fn test_class_with_pub_method() {
let code = "class Point { pub fun get_x(&self) -> f64 { self.x } }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Class with pub method should parse");
}
#[test]
fn test_nested_class_error() {
let code = "class Outer { class Inner { } }";
let result = Parser::new(code).parse();
assert!(result.is_err(), "Nested classes should fail");
}
#[test]
fn test_impl_in_class_error() {
let code = "class MyClass { impl SomeTrait { } }";
let result = Parser::new(code).parse();
assert!(result.is_err(), "Impl in class should fail");
}
#[test]
fn test_class_with_decorated_field() {
let code = "class MyClass { @JsonIgnore value: i32 }";
let result = Parser::new(code).parse();
assert!(result.is_ok() || result.is_err());
}
use crate::frontend::ast::{Expr, ExprKind};
use crate::frontend::parser::Result;
fn parse(code: &str) -> Result<Expr> {
Parser::new(code).parse()
}
fn get_block_exprs(expr: &Expr) -> Option<&Vec<Expr>> {
match &expr.kind {
ExprKind::Block(exprs) => Some(exprs),
_ => None,
}
}
#[test]
fn test_class_produces_class_exprkind() {
let expr = parse("class Foo { }").unwrap();
if let Some(exprs) = get_block_exprs(&expr) {
assert!(
matches!(&exprs[0].kind, ExprKind::Class { .. }),
"Should produce Class ExprKind"
);
}
}
#[test]
fn test_class_single_char_name() {
let result = parse("class A { }");
assert!(result.is_ok(), "Single char class name should parse");
}
#[test]
fn test_class_long_name() {
let result = parse("class VeryLongClassNameWithManyChars { }");
assert!(result.is_ok(), "Long class name should parse");
}
#[test]
fn test_class_underscore_name() {
let result = parse("class _InternalClass { }");
assert!(result.is_ok(), "Underscore prefix class should parse");
}
#[test]
fn test_class_numbers_in_name() {
let result = parse("class Vector3D { }");
assert!(result.is_ok(), "Class with numbers should parse");
}
#[test]
fn test_class_one_field() {
let result = parse("class Point { x: i32 }");
assert!(result.is_ok(), "One field should parse");
}
#[test]
fn test_class_two_fields() {
let result = parse("class Point { x: i32, y: i32 }");
assert!(result.is_ok(), "Two fields should parse");
}
#[test]
fn test_class_three_fields() {
let result = parse("class Point { x: i32, y: i32, z: i32 }");
assert!(result.is_ok(), "Three fields should parse");
}
#[test]
fn test_class_field_i32() {
let result = parse("class Data { value: i32 }");
assert!(result.is_ok(), "i32 field should parse");
}
#[test]
fn test_class_field_f64() {
let result = parse("class Data { value: f64 }");
assert!(result.is_ok(), "f64 field should parse");
}
#[test]
fn test_class_field_string() {
let result = parse("class Data { name: String }");
assert!(result.is_ok(), "String field should parse");
}
#[test]
fn test_class_field_bool() {
let result = parse("class Data { flag: bool }");
assert!(result.is_ok(), "bool field should parse");
}
#[test]
fn test_class_field_option() {
let result = parse("class Data { maybe: Option<i32> }");
assert!(result.is_ok(), "Option field should parse");
}
#[test]
fn test_class_field_vec() {
let result = parse("class Data { items: Vec<i32> }");
assert!(result.is_ok(), "Vec field should parse");
}
#[test]
fn test_class_method_no_params() {
let result = parse("class Foo { fun get(&self) { } }");
assert!(result.is_ok(), "Method no params should parse");
}
#[test]
fn test_class_method_one_param() {
let result = parse("class Foo { fun set(&mut self, v: i32) { } }");
assert!(result.is_ok(), "Method one param should parse");
}
#[test]
fn test_class_method_two_params() {
let result = parse("class Foo { fun compute(&self, a: i32, b: i32) { } }");
assert!(result.is_ok(), "Method two params should parse");
}
#[test]
fn test_class_static_method_no_params() {
let result = parse("class Foo { static fun create() { } }");
assert!(result.is_ok(), "Static no params should parse");
}
#[test]
fn test_class_static_method_with_params() {
let result = parse("class Foo { static fun create(a: i32) { } }");
assert!(result.is_ok(), "Static with params should parse");
}
#[test]
fn test_class_constructor_no_params() {
let result = parse("class Foo { new() { } }");
assert!(result.is_ok(), "Constructor no params should parse");
}
#[test]
fn test_class_constructor_one_param() {
let result = parse("class Foo { new(x: i32) { } }");
assert!(result.is_ok(), "Constructor one param should parse");
}
#[test]
fn test_class_constructor_three_params() {
let result = parse("class Foo { new(a: i32, b: i32, c: i32) { } }");
assert!(result.is_ok(), "Constructor three params should parse");
}
#[test]
fn test_class_init_constructor_no_params() {
let result = parse("class Foo { init() { } }");
assert!(result.is_ok(), "Init no params should parse");
}
#[test]
fn test_class_init_constructor_with_params() {
let result = parse("class Foo { init(v: i32) { self.v = v } }");
assert!(result.is_ok(), "Init with params should parse");
}
#[test]
fn test_class_extends_one() {
let result = parse("class Child : Parent { }");
assert!(result.is_ok(), "Extends one should parse");
}
#[test]
fn test_class_extends_with_trait() {
let result = parse("class Child : Parent + Trait1 { }");
assert!(result.is_ok(), "Extends with trait should parse");
}
#[test]
fn test_class_extends_with_two_traits() {
let result = parse("class Child : Parent + Trait1 + Trait2 { }");
assert!(result.is_ok(), "Extends with two traits should parse");
}
#[test]
fn test_class_extends_generic_parent() {
let result = parse("class IntList : List<i32> { }");
assert!(result.is_ok(), "Extends generic parent should parse");
}
#[test]
fn test_class_generic_one() {
let result = parse("class Box<T> { }");
assert!(result.is_ok(), "One generic should parse");
}
#[test]
fn test_class_generic_two() {
let result = parse("class Pair<A, B> { }");
assert!(result.is_ok(), "Two generics should parse");
}
#[test]
fn test_class_generic_three() {
let result = parse("class Triple<A, B, C> { }");
assert!(result.is_ok(), "Three generics should parse");
}
#[test]
fn test_class_generic_with_field() {
let result = parse("class Box<T> { value: T }");
assert!(result.is_ok(), "Generic with field should parse");
}
#[test]
fn test_class_generic_with_method() {
let result = parse("class Box<T> { fun get(&self) -> T { self.value } }");
assert!(result.is_ok(), "Generic with method should parse");
}
#[test]
fn test_class_pub_field_only() {
let result = parse("class Foo { pub x: i32 }");
assert!(result.is_ok(), "Pub field should parse");
}
#[test]
fn test_class_mut_field_only() {
let result = parse("class Foo { mut x: i32 }");
assert!(result.is_ok(), "Mut field should parse");
}
#[test]
fn test_class_pub_method() {
let result = parse("class Foo { pub fun get(&self) { } }");
assert!(result.is_ok(), "Pub method should parse");
}
#[test]
fn test_class_pub_static_method() {
let result = parse("class Foo { pub static fun create() { } }");
assert!(result.is_ok(), "Pub static method should parse");
}
#[test]
fn test_class_fields_and_methods() {
let result = parse("class Point { x: i32, y: i32, fun len(&self) { } }");
assert!(result.is_ok(), "Fields and methods should parse");
}
#[test]
fn test_class_constructor_and_method() {
let result = parse("class Foo { new() { } fun get(&self) { } }");
assert!(result.is_ok(), "Constructor and method should parse");
}
#[test]
fn test_class_all_elements() {
let result = parse("class Foo { x: i32, new(x: i32) { self.x = x } fun get(&self) -> i32 { self.x } static fun zero() { } }");
assert!(result.is_ok(), "All elements should parse");
}
#[test]
fn test_class_async_method() {
let result = parse("class Client { async fun fetch(&self) { } }");
assert!(result.is_ok(), "Async method should parse");
}
#[test]
fn test_class_generic_constraint() {
let result = parse("class Container<T: Clone> { value: T }");
assert!(result.is_ok(), "Generic constraint should parse");
}
#[test]
fn test_class_multiple_same_type_fields() {
let result = parse("class Vec3 { x: f64, y: f64, z: f64 }");
assert!(result.is_ok(), "Multiple same type fields should parse");
}
#[test]
fn test_class_method_returns_self() {
let result = parse("class Builder { fun with_value(&mut self, v: i32) -> Self { self } }");
assert!(result.is_ok(), "Method returning Self should parse");
}
#[test]
fn test_class_impl_style_method() {
let result = parse("class Foo { fun compute(&self, x: i32, y: i32) -> i32 { x + y } }");
assert!(result.is_ok(), "Impl style method should parse");
}
#[test]
fn test_class_default_field() {
let result = parse("class Config { debug: bool = false }");
assert!(result.is_ok(), "Default field value should parse");
}
#[test]
fn test_class_method_optional_return() {
let result = parse("class Cache { fun get(&self, key: str) -> Option<T> { None } }");
assert!(result.is_ok(), "Optional return type should parse");
}
#[test]
fn test_class_private_method() {
let result = parse("class Service { fun internal(&self) { } }");
assert!(result.is_ok(), "Private method should parse");
}
#[test]
fn test_empty_class_with_whitespace() {
let result = parse("class Empty {\n\n}");
assert!(result.is_ok(), "Empty class with whitespace should parse");
}
#[test]
fn test_class_both_constructors() {
let result = parse("class Dual { new() { } init() { } }");
assert!(result.is_ok(), "Both constructors should parse");
}
}