use super::{bail, utils, Expr, ExprKind, Param, ParserState, Result, Span, Token, Type, TypeKind};
use crate::frontend::ast::{DataFrameOp, Literal, Pattern};
fn skip_comments(state: &mut ParserState) {
while matches!(
state.tokens.peek(),
Some((
Token::LineComment(_)
| Token::BlockComment(_)
| Token::DocComment(_)
| Token::HashComment(_),
_
))
) {
state.tokens.advance();
}
}
pub fn parse_function(state: &mut ParserState) -> Result<Expr> {
parse_function_with_visibility(state, false)
}
pub fn parse_function_with_visibility(state: &mut ParserState, is_pub: bool) -> Result<Expr> {
let start_span = state.tokens.advance().expect("checked by parser logic").1;
let name = parse_function_name(state);
let mut type_params = parse_optional_type_params(state)?;
let params = utils::parse_params(state)?;
let return_type = parse_optional_return_type(state)?;
let where_bounds = parse_optional_where_clause(state)?;
for bound in where_bounds {
if let Some(colon_pos) = bound.find(':') {
let param_name = bound[..colon_pos].trim();
if let Some(existing) = type_params
.iter_mut()
.find(|p| p.split(':').next().map(str::trim) == Some(param_name))
{
if existing.contains(':') {
let new_bounds = bound[colon_pos + 1..].trim();
existing.push_str(" + ");
existing.push_str(new_bounds);
} else {
*existing = bound;
}
} else {
type_params.push(bound);
}
}
}
skip_comments(state);
let body = super::parse_expr_recursive(state)?;
Ok(Expr::new(
ExprKind::Function {
name,
type_params,
params,
return_type,
body: Box::new(body),
is_async: false,
is_pub,
},
start_span,
))
}
fn parse_function_name(state: &mut ParserState) -> String {
if let Some((Token::Identifier(n), _)) = state.tokens.peek() {
let name = n.clone();
state.tokens.advance();
name
} else {
"anonymous".to_string()
}
}
fn parse_optional_type_params(state: &mut ParserState) -> Result<Vec<String>> {
if matches!(state.tokens.peek(), Some((Token::Less, _))) {
utils::parse_type_parameters(state)
} else {
Ok(Vec::new())
}
}
fn parse_optional_return_type(state: &mut ParserState) -> Result<Option<Type>> {
if matches!(state.tokens.peek(), Some((Token::Arrow, _))) {
state.tokens.advance();
Ok(Some(utils::parse_type(state)?))
} else {
Ok(None)
}
}
fn parse_optional_where_clause(state: &mut ParserState) -> Result<Vec<String>> {
if matches!(state.tokens.peek(), Some((Token::Where, _))) {
parse_where_clause(state)
} else {
Ok(Vec::new())
}
}
fn parse_lambda_params(state: &mut ParserState) -> Result<Vec<Param>> {
let mut params = Vec::new();
while !is_at_param_end(state) {
if !try_append_lambda_param(state, &mut params)? {
break;
}
}
Ok(params)
}
fn try_append_lambda_param(state: &mut ParserState, params: &mut Vec<Param>) -> Result<bool> {
let Some(param) = try_parse_single_lambda_param(state)? else {
return Ok(false);
};
params.push(param);
Ok(consume_comma_if_present(state))
}
fn is_at_param_end(state: &mut ParserState) -> bool {
matches!(state.tokens.peek(), Some((Token::Pipe, _)))
}
fn try_parse_single_lambda_param(state: &mut ParserState) -> Result<Option<Param>> {
let Some(param_name) = try_parse_param_name(state)? else {
return Ok(None);
};
let param_type = parse_optional_type_annotation(state)?;
Ok(Some(create_lambda_param(param_name, param_type)))
}
fn try_parse_param_name(state: &mut ParserState) -> Result<Option<String>> {
if let Some((Token::Identifier(n), _)) = state.tokens.peek() {
let name = n.clone();
state.tokens.advance();
Ok(Some(name))
} else {
Ok(None)
}
}
fn parse_optional_type_annotation(state: &mut ParserState) -> Result<Type> {
if matches!(state.tokens.peek(), Some((Token::Colon, _))) {
state.tokens.advance();
utils::parse_type(state)
} else {
Ok(Type {
kind: TypeKind::Named("_".to_string()),
span: Span { start: 0, end: 0 },
})
}
}
fn create_lambda_param(name: String, ty: Type) -> Param {
Param {
pattern: Pattern::Identifier(name),
ty,
span: Span { start: 0, end: 0 },
is_mutable: false,
default_value: None,
}
}
fn consume_comma_if_present(state: &mut ParserState) -> bool {
if matches!(state.tokens.peek(), Some((Token::Comma, _))) {
state.tokens.advance();
true
} else {
false
}
}
pub fn parse_empty_lambda(state: &mut ParserState) -> Result<Expr> {
let start_span = state.tokens.advance().expect("checked by parser logic").1; let body = super::parse_expr_recursive(state)?;
Ok(Expr::new(
ExprKind::Lambda {
params: Vec::new(),
body: Box::new(body),
},
start_span,
))
}
pub fn parse_lambda(state: &mut ParserState) -> Result<Expr> {
let start_span = state
.tokens
.peek()
.map_or(Span { start: 0, end: 0 }, |(_, s)| *s);
let params = if matches!(state.tokens.peek(), Some((Token::Backslash, _))) {
parse_backslash_lambda(state)?
} else {
parse_pipe_lambda(state)?
};
let body = super::parse_expr_recursive(state)?;
Ok(Expr::new(
ExprKind::Lambda {
params,
body: Box::new(body),
},
start_span,
))
}
fn parse_backslash_lambda(state: &mut ParserState) -> Result<Vec<Param>> {
state.tokens.advance(); let params = parse_simple_params(state)?;
state
.tokens
.expect(&Token::Arrow)
.map_err(|e| anyhow::anyhow!("In backslash lambda after params: {e}"))?;
Ok(params)
}
fn parse_pipe_lambda(state: &mut ParserState) -> Result<Vec<Param>> {
state.tokens.advance(); if matches!(state.tokens.peek(), Some((Token::Pipe, _))) {
state.tokens.advance(); return Ok(Vec::new());
}
let params = parse_lambda_params(state)?;
if !matches!(state.tokens.peek(), Some((Token::Pipe, _))) {
bail!("Expected '|' after lambda parameters");
}
state.tokens.advance(); Ok(params)
}
fn parse_simple_params(state: &mut ParserState) -> Result<Vec<Param>> {
let mut params = Vec::new();
if let Some((Token::Identifier(name), _)) = state.tokens.peek() {
params.push(create_simple_param(name.clone()));
state.tokens.advance();
while matches!(state.tokens.peek(), Some((Token::Comma, _))) {
state.tokens.advance(); if let Some((Token::Identifier(name), _)) = state.tokens.peek() {
params.push(create_simple_param(name.clone()));
state.tokens.advance();
}
}
}
Ok(params)
}
fn create_simple_param(name: String) -> Param {
Param {
pattern: Pattern::Identifier(name),
ty: Type {
kind: TypeKind::Named("Any".to_string()),
span: Span { start: 0, end: 0 },
},
span: Span { start: 0, end: 0 },
is_mutable: false,
default_value: None,
}
}
pub fn parse_call(state: &mut ParserState, func: Expr) -> Result<Expr> {
state.tokens.advance(); let (args, named_args) = parse_arguments_list(state)?;
state.tokens.expect(&Token::RightParen)?;
build_call_expression(func, args, named_args)
}
fn build_call_expression(
func: Expr,
args: Vec<Expr>,
named_args: Vec<(String, Expr)>,
) -> Result<Expr> {
if named_args.is_empty() {
Ok(Expr {
kind: ExprKind::Call {
func: Box::new(func),
args,
},
span: Span { start: 0, end: 0 },
attributes: Vec::new(),
leading_comments: Vec::new(),
trailing_comment: None,
})
} else {
build_struct_literal_call(func, named_args)
}
}
fn build_struct_literal_call(func: Expr, named_args: Vec<(String, Expr)>) -> Result<Expr> {
if let ExprKind::Identifier(name) = &func.kind {
let fields = named_args.into_iter().collect();
Ok(Expr {
kind: ExprKind::StructLiteral {
name: name.clone(),
fields,
base: None,
},
span: Span { start: 0, end: 0 },
attributes: Vec::new(),
leading_comments: Vec::new(),
trailing_comment: None,
})
} else {
Ok(Expr {
kind: ExprKind::Call {
func: Box::new(func),
args: Vec::new(),
},
span: Span { start: 0, end: 0 },
attributes: Vec::new(),
leading_comments: Vec::new(),
trailing_comment: None,
})
}
}
#[allow(clippy::too_many_lines)]
pub fn parse_method_call(state: &mut ParserState, receiver: Expr) -> Result<Expr> {
if let Some((Token::Await, _)) = state.tokens.peek() {
state.tokens.advance(); return Ok(Expr {
kind: ExprKind::Await {
expr: Box::new(receiver),
},
span: Span { start: 0, end: 0 },
attributes: Vec::new(),
leading_comments: Vec::new(),
trailing_comment: None,
});
}
state.skip_comments();
match state.tokens.peek() {
Some((Token::Identifier(name), _)) => {
let method = name.clone();
state.tokens.advance();
parse_method_or_field_access(state, receiver, method)
}
Some((Token::Send, _)) => {
state.tokens.advance();
parse_method_or_field_access(state, receiver, "send".to_string())
}
Some((Token::Ask, _)) => {
state.tokens.advance();
parse_method_or_field_access(state, receiver, "ask".to_string())
}
Some((Token::Integer(index), _)) => {
let index = index.clone();
state.tokens.advance();
Ok(Expr {
kind: ExprKind::FieldAccess {
object: Box::new(receiver),
field: index,
},
span: Span { start: 0, end: 0 },
attributes: Vec::new(),
leading_comments: Vec::new(),
trailing_comment: None,
})
}
_ => {
bail!("Expected method name, tuple index, or 'await' after '.'");
}
}
}
pub fn parse_optional_method_call(state: &mut ParserState, receiver: Expr) -> Result<Expr> {
state.skip_comments();
match state.tokens.peek() {
Some((Token::Identifier(name), _)) => {
let method = name.clone();
state.tokens.advance();
parse_optional_method_or_field_access(state, receiver, method)
}
Some((Token::Send, _)) => {
state.tokens.advance();
parse_optional_method_or_field_access(state, receiver, "send".to_string())
}
Some((Token::Ask, _)) => {
state.tokens.advance();
parse_optional_method_or_field_access(state, receiver, "ask".to_string())
}
Some((Token::Integer(index), _)) => {
let index = index.clone();
state.tokens.advance();
Ok(Expr {
kind: ExprKind::OptionalFieldAccess {
object: Box::new(receiver),
field: index,
},
span: Span { start: 0, end: 0 },
attributes: Vec::new(),
leading_comments: Vec::new(),
trailing_comment: None,
})
}
_ => {
bail!("Expected method name or tuple index after '?.'");
}
}
}
fn parse_method_or_field_access(
state: &mut ParserState,
receiver: Expr,
method: String,
) -> Result<Expr> {
let mut method_name = method;
if matches!(state.tokens.peek(), Some((Token::ColonColon, _))) {
state.tokens.advance(); if matches!(state.tokens.peek(), Some((Token::Less, _))) {
let turbofish =
super::expressions::expressions_helpers::identifiers::parse_turbofish_generics(
state,
)?;
method_name.push_str("::");
method_name.push_str(&turbofish);
} else {
bail!("Expected '<' after '::' in turbofish syntax");
}
}
if matches!(state.tokens.peek(), Some((Token::LeftParen, _))) {
parse_method_call_access(state, receiver, method_name)
} else {
Ok(create_field_access(receiver, method_name))
}
}
fn parse_method_call_access(
state: &mut ParserState,
receiver: Expr,
method: String,
) -> Result<Expr> {
state.tokens.advance(); let args = parse_method_arguments(state)?;
state.tokens.expect(&Token::RightParen)?;
if is_dataframe_method(&method) {
handle_dataframe_method(receiver, method, args)
} else {
Ok(create_method_call(receiver, method, args))
}
}
fn parse_method_arguments(state: &mut ParserState) -> Result<Vec<Expr>> {
let (mut args, named_args) = parse_arguments_list(state)?;
if !named_args.is_empty() {
args.push(convert_named_args_to_object(state, named_args));
}
Ok(args)
}
fn convert_named_args_to_object(state: &mut ParserState, named_args: Vec<(String, Expr)>) -> Expr {
use crate::frontend::ast::ObjectField;
let fields = named_args
.into_iter()
.map(|(name, value)| ObjectField::KeyValue { key: name, value })
.collect();
let span = if let Some((_, span)) = state.tokens.peek() {
*span
} else {
crate::frontend::ast::Span::new(0, 0)
};
Expr::new(ExprKind::ObjectLiteral { fields }, span)
}
fn parse_arguments_list(state: &mut ParserState) -> Result<(Vec<Expr>, Vec<(String, Expr)>)> {
let mut args = Vec::new();
let mut named_args = Vec::new();
while !is_at_argument_list_end(state) {
parse_single_argument(state, &mut args, &mut named_args)?;
if !handle_argument_separator(state) {
break;
}
}
Ok((args, named_args))
}
fn is_at_argument_list_end(state: &mut ParserState) -> bool {
matches!(state.tokens.peek(), Some((Token::RightParen, _)))
}
fn parse_single_argument(
state: &mut ParserState,
args: &mut Vec<Expr>,
named_args: &mut Vec<(String, Expr)>,
) -> Result<()> {
if let Some((name, value)) = try_parse_named_argument(state)? {
named_args.push((name, value));
} else {
args.push(super::parse_expr_recursive(state)?);
}
Ok(())
}
fn try_parse_named_argument(state: &mut ParserState) -> Result<Option<(String, Expr)>> {
if let Some((Token::Identifier(name), _)) = state.tokens.peek() {
let name_clone = name.clone();
let saved_pos = state.tokens.position();
state.tokens.advance();
if matches!(state.tokens.peek(), Some((Token::Colon, _))) {
state.tokens.advance(); let value = super::parse_expr_recursive(state)?;
return Ok(Some((name_clone, value)));
}
state.tokens.set_position(saved_pos);
}
Ok(None)
}
fn handle_argument_separator(state: &mut ParserState) -> bool {
if matches!(state.tokens.peek(), Some((Token::Comma, _))) {
state.tokens.advance();
true
} else {
false
}
}
fn is_dataframe_method(method: &str) -> bool {
matches!(
method,
"groupby"
| "group_by"
| "agg"
| "pivot"
| "melt"
| "join"
| "rolling"
| "shift"
| "diff"
| "pct_change"
| "corr"
| "cov"
)
}
fn handle_dataframe_method(receiver: Expr, method: String, args: Vec<Expr>) -> Result<Expr> {
let operation = match method.as_str() {
"groupby" | "group_by" => DataFrameOp::GroupBy(extract_groupby_columns(args)),
_ => return Ok(create_method_call(receiver, method, args)),
};
Ok(Expr {
kind: ExprKind::DataFrameOperation {
source: Box::new(receiver),
operation,
},
span: Span { start: 0, end: 0 },
attributes: Vec::new(),
leading_comments: Vec::new(),
trailing_comment: None,
})
}
fn extract_select_columns(args: Vec<Expr>) -> Vec<String> {
let mut columns = Vec::new();
for arg in args {
match arg.kind {
ExprKind::Identifier(name) => {
columns.push(name);
}
ExprKind::List(items) => {
for item in items {
if let ExprKind::Literal(Literal::String(col_name)) = item.kind {
columns.push(col_name);
}
}
}
ExprKind::Literal(Literal::String(col_name)) => {
columns.push(col_name);
}
_ => {}
}
}
columns
}
fn extract_groupby_columns(args: Vec<Expr>) -> Vec<String> {
args.into_iter()
.filter_map(|arg| {
if let ExprKind::Identifier(name) = arg.kind {
Some(name)
} else {
None
}
})
.collect()
}
fn create_method_call(receiver: Expr, method: String, args: Vec<Expr>) -> Expr {
Expr {
kind: ExprKind::MethodCall {
receiver: Box::new(receiver),
method,
args,
},
span: Span { start: 0, end: 0 },
attributes: Vec::new(),
leading_comments: Vec::new(),
trailing_comment: None,
}
}
fn create_field_access(receiver: Expr, field: String) -> Expr {
Expr {
kind: ExprKind::FieldAccess {
object: Box::new(receiver),
field,
},
span: Span { start: 0, end: 0 },
attributes: Vec::new(),
leading_comments: Vec::new(),
trailing_comment: None,
}
}
fn parse_optional_method_or_field_access(
state: &mut ParserState,
receiver: Expr,
method: String,
) -> Result<Expr> {
if is_method_call(state) {
parse_optional_method_call_syntax(state, receiver, method)
} else {
Ok(create_optional_field_access(receiver, method))
}
}
fn is_method_call(state: &mut ParserState) -> bool {
matches!(state.tokens.peek(), Some((Token::LeftParen, _)))
}
fn parse_optional_method_call_syntax(
state: &mut ParserState,
receiver: Expr,
method: String,
) -> Result<Expr> {
state.tokens.advance(); let args = parse_optional_method_args(state)?;
state.tokens.expect(&Token::RightParen)?;
Ok(create_optional_method_call(receiver, method, args))
}
fn parse_optional_method_args(state: &mut ParserState) -> Result<Vec<Expr>> {
let mut args = Vec::new();
while !matches!(state.tokens.peek(), Some((Token::RightParen, _))) {
args.push(super::parse_expr_recursive(state)?);
if !consume_comma_if_present(state) {
break;
}
}
Ok(args)
}
fn create_optional_method_call(receiver: Expr, method: String, args: Vec<Expr>) -> Expr {
Expr {
kind: ExprKind::OptionalMethodCall {
receiver: Box::new(receiver),
method,
args,
},
span: Span { start: 0, end: 0 },
attributes: Vec::new(),
leading_comments: Vec::new(),
trailing_comment: None,
}
}
fn create_optional_field_access(receiver: Expr, field: String) -> Expr {
Expr {
kind: ExprKind::OptionalFieldAccess {
object: Box::new(receiver),
field,
},
span: Span { start: 0, end: 0 },
attributes: Vec::new(),
leading_comments: Vec::new(),
trailing_comment: None,
}
}
fn parse_where_clause(state: &mut ParserState) -> Result<Vec<String>> {
state.tokens.advance();
let mut bounds = Vec::new();
while let Some(bound) = parse_single_trait_bound(state)? {
bounds.push(bound);
}
Ok(bounds)
}
fn parse_single_trait_bound(state: &mut ParserState) -> Result<Option<String>> {
let type_param = match state.tokens.peek() {
Some((Token::Identifier(name), _)) => name.clone(),
_ => return Ok(None),
};
state.tokens.advance();
if !matches!(state.tokens.peek(), Some((Token::Colon, _))) {
return Ok(None);
}
state.tokens.advance();
let mut traits = String::new();
while !is_trait_bound_end(state) {
if let Some((token, _)) = state.tokens.peek() {
if !traits.is_empty() && !matches!(token, Token::Plus | Token::Less | Token::Greater) {
traits.push(' ');
}
traits.push_str(&token_to_string(token));
state.tokens.advance();
} else {
break;
}
}
let has_more = matches!(state.tokens.peek(), Some((Token::Comma, _)));
if has_more {
state.tokens.advance(); }
Ok(Some(format!("{type_param}: {traits}")))
}
fn token_to_string(token: &Token) -> String {
match token {
Token::Identifier(s) => s.clone(),
Token::Plus => "+".to_string(),
Token::Less => "<".to_string(),
Token::Greater => ">".to_string(),
Token::Comma => ",".to_string(),
Token::Lifetime(lt) => lt.clone(),
_ => format!("{token:?}"),
}
}
fn consume_trait_bound_tokens(state: &mut ParserState) -> Result<bool> {
while should_continue_parsing_trait_bound(state)? {
}
Ok(is_comma_delimiter(state))
}
fn should_continue_parsing_trait_bound(state: &mut ParserState) -> Result<bool> {
if is_trait_bound_end(state) {
return Ok(false);
}
consume_trait_bound_token_if_present(state);
Ok(true)
}
fn is_trait_bound_end(state: &mut ParserState) -> bool {
matches!(
state.tokens.peek(),
Some((Token::Comma | Token::LeftBrace, _))
)
}
fn is_comma_delimiter(state: &mut ParserState) -> bool {
matches!(state.tokens.peek(), Some((Token::Comma, _)))
}
fn try_handle_trait_bound_delimiter(state: &mut ParserState) -> Option<bool> {
match state.tokens.peek() {
Some((Token::Comma, _)) => {
state.tokens.advance();
Some(true) }
Some((Token::LeftBrace, _)) => {
Some(false) }
_ => None,
}
}
fn consume_trait_bound_token_if_present(state: &mut ParserState) -> bool {
if state.tokens.peek().is_some() {
state.tokens.advance();
true
} else {
false
}
}
#[cfg(test)]
mod tests {
use crate::frontend::parser::Parser;
#[test]
fn test_parse_simple_function() {
let mut parser = Parser::new("fun add(x: i32, y: i32) -> i32 { x + y }");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse simple function");
}
#[test]
fn test_parse_function_no_params() {
let mut parser = Parser::new("fun hello() { println(\"Hello\") }");
let result = parser.parse();
assert!(
result.is_ok(),
"Failed to parse function without parameters"
);
}
#[test]
fn test_parse_function_no_return_type() {
let mut parser = Parser::new("fun greet(name: String) { println(name) }");
let result = parser.parse();
assert!(
result.is_ok(),
"Failed to parse function without return type"
);
}
#[test]
fn test_parse_anonymous_function() {
let mut parser = Parser::new("fun (x: i32) -> i32 { x * 2 }");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse anonymous function");
}
#[test]
fn test_parse_generic_function() {
let mut parser = Parser::new("fun identity<T>(value: T) -> T { value }");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse generic function");
}
#[test]
fn test_parse_function_multiple_params() {
let mut parser = Parser::new("fun sum(a: i32, b: i32, c: i32) -> i32 { a + b + c }");
let result = parser.parse();
assert!(
result.is_ok(),
"Failed to parse function with multiple parameters"
);
}
#[test]
fn test_parse_lambda_simple() {
let mut parser = Parser::new("|x| x + 1");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse simple lambda");
}
#[test]
fn test_parse_lambda_multiple_params() {
let mut parser = Parser::new("|x, y| x + y");
let result = parser.parse();
assert!(
result.is_ok(),
"Failed to parse lambda with multiple parameters"
);
}
#[test]
fn test_parse_lambda_with_types() {
let mut parser = Parser::new("|x, y| x + y");
let result = parser.parse();
assert!(
result.is_ok(),
"Failed to parse lambda with type annotations"
);
}
#[test]
fn test_parse_lambda_no_params() {
let mut parser = Parser::new("|| 42");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse lambda without parameters");
}
#[test]
fn test_parse_fat_arrow_lambda() {
let mut parser = Parser::new("x => x * 2");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse fat arrow lambda");
}
#[test]
fn test_parse_fat_arrow_lambda_multiple() {
let mut parser = Parser::new("(x, y) => x + y");
let result = parser.parse();
assert!(
result.is_ok(),
"Failed to parse fat arrow lambda with multiple params"
);
}
#[test]
fn test_parse_function_call_no_args() {
let mut parser = Parser::new("print()");
let result = parser.parse();
assert!(
result.is_ok(),
"Failed to parse function call without arguments"
);
}
#[test]
fn test_parse_function_call_single_arg() {
let mut parser = Parser::new("sqrt(16)");
let result = parser.parse();
assert!(
result.is_ok(),
"Failed to parse function call with single argument"
);
}
#[test]
fn test_parse_function_call_multiple_args() {
let mut parser = Parser::new("max(1, 2, 3)");
let result = parser.parse();
assert!(
result.is_ok(),
"Failed to parse function call with multiple arguments"
);
}
#[test]
fn test_parse_function_call_named_args() {
let mut parser = Parser::new("create(\"test\", 42)");
let result = parser.parse();
assert!(
result.is_ok(),
"Failed to parse function call with named arguments"
);
}
#[test]
fn test_parse_method_call_no_args() {
let mut parser = Parser::new("obj.method()");
let result = parser.parse();
assert!(
result.is_ok(),
"Failed to parse method call without arguments"
);
}
#[test]
fn test_parse_method_call_with_args() {
let mut parser = Parser::new("list.append(42)");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse method call with arguments");
}
#[test]
fn test_parse_chained_method_calls() {
let mut parser = Parser::new("str.trim().to_uppercase().split(\",\")");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse chained method calls");
}
#[test]
fn test_parse_safe_navigation() {
let mut parser = Parser::new("obj?.method()");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse safe navigation operator");
}
#[test]
fn test_parse_safe_navigation_chain() {
let mut parser = Parser::new("obj?.field?.method()");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse chained safe navigation");
}
#[test]
fn test_parse_function_with_default_params() {
let mut parser = Parser::new("fun greet(name: String = \"World\") { println(name) }");
let result = parser.parse();
assert!(
result.is_ok(),
"Failed to parse function with default parameters"
);
}
#[test]
fn test_parse_function_with_rest_params() {
let mut parser = Parser::new("fun sum(numbers: Vec<i32>) -> i32 { numbers.sum() }");
let result = parser.parse();
assert!(
result.is_ok(),
"Failed to parse function with rest parameters"
);
}
#[test]
fn test_parse_nested_function_calls() {
let mut parser = Parser::new("outer(inner(deep(42)))");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse nested function calls");
}
#[test]
fn test_parse_function_call_with_lambda() {
let mut parser = Parser::new("map(list, |x| x * 2)");
let result = parser.parse();
assert!(
result.is_ok(),
"Failed to parse function call with lambda argument"
);
}
#[test]
fn test_parse_function_with_block_body() {
let mut parser = Parser::new("fun complex(x: i32) -> i32 { let y = x + 1; y * 2 }");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse function with block body");
}
#[test]
fn test_parse_recursive_function() {
let mut parser = Parser::new(
"fun factorial(n: i32) -> i32 { if n <= 1 { 1 } else { n * factorial(n - 1) } }",
);
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse recursive function");
}
#[test]
fn test_parse_higher_order_function() {
let mut parser = Parser::new("fun apply(f: fn(i32) -> i32, x: i32) -> i32 { f(x) }");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse higher-order function");
}
#[test]
fn test_parse_closure_capture() {
let mut parser = Parser::new("{ let x = 10; |y| x + y }");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse closure with capture");
}
#[test]
fn test_parse_iife() {
let mut parser = Parser::new("(|x| x * 2)(5)");
let result = parser.parse();
assert!(
result.is_ok(),
"Failed to parse immediately invoked function expression"
);
}
#[test]
fn test_tuple_access_with_integer_index() {
let mut parser = Parser::new("let t = (1, 2, 3); t.0");
let result = parser.parse();
assert!(
result.is_ok(),
"Should parse tuple access with integer index t.0"
);
}
#[test]
fn test_tuple_access_multiple_indices() {
let mut parser = Parser::new("let t = (\"a\", \"b\", \"c\"); t.1");
let result = parser.parse();
assert!(result.is_ok(), "Should parse tuple access t.1");
}
#[test]
fn test_tuple_access_third_element() {
let mut parser = Parser::new("let t = (1, 2, 3); t.2");
let result = parser.parse();
assert!(result.is_ok(), "Should parse tuple access t.2");
}
#[test]
fn test_parse_anonymous_function_empty_params() {
let mut parser = Parser::new("fun () { 42 }");
let result = parser.parse();
assert!(
result.is_ok(),
"Should parse anonymous function with empty params"
);
}
#[test]
fn test_parse_async_function() {
let mut parser = Parser::new("async fun fetch_data() { await get_data() }");
let result = parser.parse();
assert!(result.is_ok(), "Should parse async function");
}
#[test]
fn test_parse_function_generic_type() {
let mut parser = Parser::new("fun identity<T>(x: T) -> T { x }");
let result = parser.parse();
assert!(result.is_ok(), "Should parse function with generic type");
}
#[test]
fn test_parse_function_multiple_generics() {
let mut parser = Parser::new("fun pair<T, U>(a: T, b: U) -> (T, U) { (a, b) }");
let result = parser.parse();
assert!(
result.is_ok(),
"Should parse function with multiple generics"
);
}
#[test]
fn test_parse_function_where_clause() {
let mut parser =
Parser::new("fun print_item<T>(item: T) where T: Display { println(item) }");
let result = parser.parse();
assert!(result.is_ok(), "Should parse function with where clause");
}
#[test]
fn test_parse_pub_function() {
let mut parser = Parser::new("pub fun add(a: i32, b: i32) -> i32 { a + b }");
let result = parser.parse();
assert!(result.is_ok(), "Should parse public function");
}
#[test]
fn test_parse_function_complex_return() {
let mut parser = Parser::new("fun get_result() -> Result<i32, String> { Ok(42) }");
let result = parser.parse();
assert!(
result.is_ok(),
"Should parse function with Result return type"
);
}
#[test]
fn test_parse_function_option_return() {
let mut parser = Parser::new("fun find(key: String) -> Option<i32> { Some(1) }");
let result = parser.parse();
assert!(
result.is_ok(),
"Should parse function with Option return type"
);
}
#[test]
fn test_parse_multiline_lambda() {
let mut parser = Parser::new("|x| { let y = x + 1; y * 2 }");
let result = parser.parse();
assert!(result.is_ok(), "Should parse multi-line lambda");
}
#[test]
fn test_parse_lambda_with_type_annotation_and_body() {
let mut parser = Parser::new("|x: i32| -> i32 { x * 2 }");
let result = parser.parse();
assert!(
result.is_ok(),
"Should parse lambda with type annotations and body"
);
}
#[test]
fn test_parse_method_chain() {
let mut parser = Parser::new("x.method1().method2().method3()");
let result = parser.parse();
assert!(result.is_ok(), "Should parse method call chain");
}
#[test]
fn test_parse_method_multiple_args() {
let mut parser = Parser::new("obj.method(1, 2, 3, 4, 5)");
let result = parser.parse();
assert!(result.is_ok(), "Should parse method with multiple args");
}
#[test]
fn test_parse_function_returning_fn() {
let mut parser = Parser::new("fun make_adder(n: i32) -> fn(i32) -> i32 { |x| x + n }");
let result = parser.parse();
assert!(result.is_ok(), "Should parse function returning function");
}
#[test]
fn test_parse_function_vec_param() {
let mut parser =
Parser::new("fun sum_all(nums: Vec<i32>) -> i32 { nums.fold(0, |a, b| a + b) }");
let result = parser.parse();
assert!(result.is_ok(), "Should parse function with Vec parameter");
}
#[test]
fn test_parse_function_hashmap_param() {
let mut parser = Parser::new(
"fun get_value(map: HashMap<String, i32>, key: String) -> i32 { map[key] }",
);
let result = parser.parse();
assert!(
result.is_ok(),
"Should parse function with HashMap parameter"
);
}
#[test]
fn test_parse_zero_arg_function_call() {
let mut parser = Parser::new("get_time()");
let result = parser.parse();
assert!(result.is_ok(), "Should parse zero-argument function call");
}
#[test]
fn test_parse_function_trailing_lambda() {
let mut parser = Parser::new("list.map { |x| x * 2 }");
let result = parser.parse();
assert!(
result.is_ok() || result.is_err(),
"Should handle trailing lambda syntax"
);
}
#[test]
fn test_optional_method_call_identifier() {
let mut parser = Parser::new("obj?.method()");
let result = parser.parse();
assert!(result.is_ok(), "Optional method call should parse: {:?}", result.err());
}
#[test]
fn test_optional_field_access() {
let mut parser = Parser::new("obj?.field");
let result = parser.parse();
assert!(result.is_ok(), "Optional field access should parse: {:?}", result.err());
}
#[test]
fn test_optional_method_call_send() {
let mut parser = Parser::new("actor?.send(msg)");
let _result = parser.parse();
}
#[test]
fn test_optional_method_call_ask() {
let mut parser = Parser::new("actor?.ask(query)");
let _result = parser.parse();
}
#[test]
fn test_optional_tuple_index_access() {
let mut parser = Parser::new("tuple?.0");
let result = parser.parse();
assert!(result.is_ok(), "Optional tuple index access should parse: {:?}", result.err());
}
#[test]
fn test_optional_tuple_index_access_second() {
let mut parser = Parser::new("tuple?.1");
let result = parser.parse();
assert!(result.is_ok(), "Optional tuple index 1 should parse: {:?}", result.err());
}
#[test]
fn test_optional_chaining_multiple() {
let mut parser = Parser::new("a?.b?.c");
let result = parser.parse();
assert!(result.is_ok(), "Multiple optional chaining should parse: {:?}", result.err());
}
#[test]
fn test_optional_method_call_with_args() {
let mut parser = Parser::new("obj?.compute(1, 2, 3)");
let result = parser.parse();
assert!(result.is_ok(), "Optional method call with args should parse: {:?}", result.err());
}
#[test]
fn test_extract_select_columns_identifiers() {
use crate::frontend::ast::{Expr, ExprKind, Span};
let args = vec![
Expr::new(ExprKind::Identifier("age".to_string()), Span::default()),
Expr::new(ExprKind::Identifier("name".to_string()), Span::default()),
];
let cols = super::extract_select_columns(args);
assert_eq!(cols, vec!["age".to_string(), "name".to_string()]);
}
#[test]
fn test_extract_select_columns_string_literals() {
use crate::frontend::ast::{Expr, ExprKind, Literal, Span};
let args = vec![
Expr::new(ExprKind::Literal(Literal::String("age".to_string())), Span::default()),
Expr::new(ExprKind::Literal(Literal::String("name".to_string())), Span::default()),
];
let cols = super::extract_select_columns(args);
assert_eq!(cols, vec!["age".to_string(), "name".to_string()]);
}
#[test]
fn test_extract_select_columns_list_of_strings() {
use crate::frontend::ast::{Expr, ExprKind, Literal, Span};
let items = vec![
Expr::new(ExprKind::Literal(Literal::String("col1".to_string())), Span::default()),
Expr::new(ExprKind::Literal(Literal::String("col2".to_string())), Span::default()),
];
let args = vec![
Expr::new(ExprKind::List(items), Span::default()),
];
let cols = super::extract_select_columns(args);
assert_eq!(cols, vec!["col1".to_string(), "col2".to_string()]);
}
#[test]
fn test_extract_select_columns_mixed() {
use crate::frontend::ast::{Expr, ExprKind, Literal, Span};
let args = vec![
Expr::new(ExprKind::Identifier("age".to_string()), Span::default()),
Expr::new(ExprKind::Literal(Literal::String("name".to_string())), Span::default()),
];
let cols = super::extract_select_columns(args);
assert_eq!(cols, vec!["age".to_string(), "name".to_string()]);
}
#[test]
fn test_extract_select_columns_unknown_expr() {
use crate::frontend::ast::{Expr, ExprKind, Literal, Span};
let args = vec![
Expr::new(ExprKind::Literal(Literal::Integer(42, None)), Span::default()),
];
let cols = super::extract_select_columns(args);
assert!(cols.is_empty(), "Integer literal should not produce columns");
}
#[test]
fn test_extract_select_columns_empty() {
let cols = super::extract_select_columns(vec![]);
assert!(cols.is_empty(), "Empty args should produce empty columns");
}
}