use crate::frontend::ast::{Expr, ExprKind, Literal, MatchArm, Pattern, Span, Type};
use crate::frontend::lexer::Token;
use crate::frontend::parser::{bail, parse_expr_recursive, utils, ParserState, Result};
fn parse_variant_pattern_with_name(
state: &mut ParserState,
variant_name: String,
) -> Result<Pattern> {
state.tokens.expect(&Token::LeftParen)?;
let mut patterns = vec![];
if !matches!(state.tokens.peek(), Some((Token::RightParen, _))) {
patterns.push(parse_single_pattern(state)?);
while matches!(state.tokens.peek(), Some((Token::Comma, _))) {
state.tokens.advance();
if matches!(state.tokens.peek(), Some((Token::RightParen, _))) {
break;
}
patterns.push(parse_single_pattern(state)?);
}
}
state.tokens.expect(&Token::RightParen)?;
create_pattern_for_variant(variant_name, patterns)
}
fn create_pattern_for_variant(variant_name: String, patterns: Vec<Pattern>) -> Result<Pattern> {
if patterns.len() == 1 {
match variant_name.as_str() {
"Some" => {
return Ok(Pattern::Some(Box::new(
patterns
.into_iter()
.next()
.expect("patterns.len() == 1 so next() must return Some"),
)))
}
"Ok" => {
return Ok(Pattern::Ok(Box::new(
patterns
.into_iter()
.next()
.expect("patterns.len() == 1 so next() must return Some"),
)))
}
"Err" => {
return Ok(Pattern::Err(Box::new(
patterns
.into_iter()
.next()
.expect("patterns.len() == 1 so next() must return Some"),
)))
}
_ => {}
}
}
Ok(Pattern::TupleVariant {
path: vec![variant_name],
patterns,
})
}
pub(in crate::frontend::parser) fn parse_let_pattern(
state: &mut ParserState,
is_mutable: bool,
) -> Result<Pattern> {
match state.tokens.peek() {
Some((Token::Some, _)) => {
state.tokens.advance();
if matches!(state.tokens.peek(), Some((Token::LeftParen, _))) {
parse_variant_pattern_with_name(state, "Some".to_string())
} else {
bail!("Some must be followed by parentheses in patterns: Some(value)")
}
}
Some((Token::Ok, _)) => {
state.tokens.advance();
if matches!(state.tokens.peek(), Some((Token::LeftParen, _))) {
parse_variant_pattern_with_name(state, "Ok".to_string())
} else {
bail!("Ok must be followed by parentheses in patterns: Ok(value)")
}
}
Some((Token::Err, _)) => {
state.tokens.advance();
if matches!(state.tokens.peek(), Some((Token::LeftParen, _))) {
parse_variant_pattern_with_name(state, "Err".to_string())
} else {
bail!("Err must be followed by parentheses in patterns: Err(value)")
}
}
Some((Token::None, _)) => {
state.tokens.advance();
Ok(Pattern::None)
}
Some((Token::Identifier(name), _)) => {
let name = name.clone();
state.tokens.advance();
if matches!(state.tokens.peek(), Some((Token::LeftParen, _))) {
parse_variant_pattern_with_name(state, name)
}
else if matches!(state.tokens.peek(), Some((Token::LeftBrace, _))) {
parse_struct_pattern_with_name(state, name)
} else {
Ok(Pattern::Identifier(name))
}
}
Some((Token::DataFrame, _)) => {
state.tokens.advance();
Ok(Pattern::Identifier("df".to_string()))
}
Some((Token::Default, _)) => {
state.tokens.advance();
Ok(Pattern::Identifier("default".to_string()))
}
Some((Token::Final, _)) => {
state.tokens.advance();
Ok(Pattern::Identifier("final".to_string()))
}
Some((Token::Underscore, _)) => {
state.tokens.advance();
Ok(Pattern::Identifier("_".to_string()))
}
Some((Token::LeftParen, _)) => {
parse_tuple_pattern(state)
}
Some((Token::LeftBracket, _)) => {
parse_list_pattern(state)
}
Some((Token::LeftBrace, _)) => {
parse_struct_pattern(state)
}
_ => bail!(
"Expected identifier or pattern after 'let{}'",
if is_mutable { " mut" } else { "" }
),
}
}
fn parse_let_type_annotation(state: &mut ParserState) -> Result<Option<Type>> {
if matches!(state.tokens.peek(), Some((Token::Colon, _))) {
state.tokens.advance(); Ok(Some(utils::parse_type(state)?))
} else {
Ok(None)
}
}
fn parse_let_else_clause(state: &mut ParserState) -> Result<Option<Box<Expr>>> {
if matches!(state.tokens.peek(), Some((Token::Else, _))) {
state.tokens.advance(); if !matches!(state.tokens.peek(), Some((Token::LeftBrace, _))) {
bail!("let-else requires a block after 'else'");
}
let block = parse_expr_recursive(state)?;
Ok(Some(Box::new(block)))
} else {
Ok(None)
}
}
fn parse_let_in_clause(state: &mut ParserState, value_span: Span) -> Result<Box<Expr>> {
if matches!(state.tokens.peek(), Some((Token::In, _))) {
state.tokens.advance(); Ok(Box::new(parse_expr_recursive(state)?))
} else {
Ok(Box::new(Expr::new(
ExprKind::Literal(Literal::Unit),
value_span,
)))
}
}
fn create_let_expression(
pattern: Pattern,
type_annotation: Option<Type>,
value: Box<Expr>,
body: Box<Expr>,
is_mutable: bool,
else_block: Option<Box<Expr>>,
start_span: Span,
) -> Result<Expr> {
let end_span = body.span;
match &pattern {
Pattern::Identifier(name) => Ok(Expr::new(
ExprKind::Let {
name: name.clone(),
type_annotation,
value,
body,
is_mutable,
else_block,
},
start_span.merge(end_span),
)),
Pattern::Tuple(_) | Pattern::List(_) => {
Ok(Expr::new(
ExprKind::LetPattern {
pattern,
type_annotation,
value,
body,
is_mutable,
else_block,
},
start_span.merge(end_span),
))
}
Pattern::Wildcard
| Pattern::Literal(_)
| Pattern::QualifiedName(_)
| Pattern::Struct { .. }
| Pattern::TupleVariant { .. }
| Pattern::Range { .. }
| Pattern::Or(_)
| Pattern::Rest
| Pattern::RestNamed(_)
| Pattern::AtBinding { .. }
| Pattern::WithDefault { .. }
| Pattern::Ok(_)
| Pattern::Err(_)
| Pattern::Some(_)
| Pattern::None
| Pattern::Mut(_) => {
Ok(Expr::new(
ExprKind::LetPattern {
pattern,
type_annotation,
value,
body,
is_mutable,
else_block,
},
start_span.merge(end_span),
))
}
}
}
pub(in crate::frontend::parser) fn parse_var_statement(state: &mut ParserState) -> Result<Expr> {
super::variable_declarations::parse_var_statement(state)
}
pub(in crate::frontend::parser) fn parse_var_pattern(state: &mut ParserState) -> Result<Pattern> {
match state.tokens.peek() {
Some((Token::Identifier(name), _)) => {
let name = name.clone();
state.tokens.advance();
Ok(Pattern::Identifier(name))
}
Some((Token::DataFrame, _)) => {
state.tokens.advance();
Ok(Pattern::Identifier("df".to_string()))
}
Some((Token::Underscore, _)) => {
state.tokens.advance();
Ok(Pattern::Identifier("_".to_string()))
}
Some((Token::LeftParen, _)) => parse_tuple_pattern(state),
Some((Token::LeftBracket, _)) => parse_list_pattern(state),
_ => bail!("Expected identifier or pattern after 'var'"),
}
}
fn parse_optional_type_annotation(
state: &mut ParserState,
) -> Result<Option<crate::frontend::ast::Type>> {
if matches!(state.tokens.peek(), Some((Token::Colon, _))) {
state.tokens.advance();
Ok(Some(utils::parse_type(state)?))
} else {
Ok(None)
}
}
fn create_var_expression(
pattern: Pattern,
type_annotation: Option<crate::frontend::ast::Type>,
value: Box<Expr>,
start_span: Span,
) -> Result<Expr> {
let body = Box::new(Expr::new(ExprKind::Literal(Literal::Unit), value.span));
let end_span = value.span;
let is_mutable = true;
match &pattern {
Pattern::Identifier(name) => Ok(Expr::new(
ExprKind::Let {
name: name.clone(),
type_annotation,
value,
body,
is_mutable,
else_block: None, },
start_span.merge(end_span),
)),
_ => Ok(Expr::new(
ExprKind::LetPattern {
pattern,
type_annotation,
value,
body,
is_mutable,
else_block: None, },
start_span.merge(end_span),
)),
}
}
pub(in crate::frontend::parser) fn parse_tuple_pattern(state: &mut ParserState) -> Result<Pattern> {
state.tokens.expect(&Token::LeftParen)?;
let mut patterns = Vec::new();
while !matches!(state.tokens.peek(), Some((Token::RightParen, _))) {
let pattern = parse_single_tuple_pattern_element(state)?;
patterns.push(pattern);
if !handle_pattern_separator(state, Token::RightParen)? {
break;
}
}
state.tokens.expect(&Token::RightParen)?;
Ok(Pattern::Tuple(patterns))
}
fn parse_single_tuple_pattern_element(state: &mut ParserState) -> Result<Pattern> {
let is_mut = if matches!(state.tokens.peek(), Some((Token::Mut, _))) {
state.tokens.advance(); true
} else {
false
};
let pattern = match state.tokens.peek() {
Some((Token::Identifier(name), _)) => {
let name = name.clone();
state.tokens.advance();
Ok(Pattern::Identifier(name))
}
Some((Token::LeftParen, _)) => parse_tuple_pattern(state),
Some((Token::LeftBracket, _)) => parse_list_pattern(state),
Some((Token::LeftBrace, _)) => parse_struct_pattern(state),
Some((Token::Underscore, _)) => {
state.tokens.advance();
Ok(Pattern::Wildcard)
}
_ => bail!("Expected identifier, tuple, list, struct, or wildcard in tuple pattern"),
}?;
if is_mut {
Ok(Pattern::Mut(Box::new(pattern)))
} else {
Ok(pattern)
}
}
pub(in crate::frontend::parser) fn parse_struct_pattern(
state: &mut ParserState,
) -> Result<Pattern> {
state.tokens.advance(); parse_struct_pattern_fields(state, String::new())
}
pub(in crate::frontend::parser) fn parse_struct_pattern_with_name(
state: &mut ParserState,
name: String,
) -> Result<Pattern> {
state.tokens.advance(); parse_struct_pattern_fields(state, name)
}
fn parse_struct_pattern_fields(state: &mut ParserState, name: String) -> Result<Pattern> {
let mut fields = Vec::new();
let mut has_rest = false;
while !matches!(state.tokens.peek(), Some((Token::RightBrace, _))) {
if matches!(state.tokens.peek(), Some((Token::DotDot, _))) {
has_rest = parse_struct_rest_pattern(state)?;
break;
}
fields.push(parse_struct_field_pattern(state)?);
if !handle_struct_field_separator(state)? {
break;
}
}
state.tokens.expect(&Token::RightBrace)?;
Ok(Pattern::Struct {
name,
fields,
has_rest,
})
}
fn parse_struct_rest_pattern(state: &mut ParserState) -> Result<bool> {
state.tokens.advance();
if matches!(state.tokens.peek(), Some((Token::Comma, _))) {
state.tokens.advance();
}
if !matches!(state.tokens.peek(), Some((Token::RightBrace, _))) {
bail!("Rest pattern (..) must be the last field in struct pattern");
}
Ok(true)
}
fn parse_struct_field_pattern(
state: &mut ParserState,
) -> Result<crate::frontend::ast::StructPatternField> {
let field_name = match state.tokens.peek() {
Some((Token::Identifier(name), _)) => name.clone(),
Some((Token::Type, _)) => "type".to_string(),
Some((Token::Use, _)) => "use".to_string(),
Some((Token::Mod, _)) => "mod".to_string(),
Some((Token::Async, _)) => "async".to_string(),
Some((Token::Await, _)) => "await".to_string(),
Some((Token::Self_, _)) => "self".to_string(),
Some((Token::Super, _)) => "super".to_string(),
Some((Token::Crate, _)) => "crate".to_string(),
_ => bail!("Expected identifier or '..' in struct pattern"),
};
state.tokens.advance();
let pattern = if matches!(state.tokens.peek(), Some((Token::Colon, _))) {
state.tokens.advance();
Some(parse_match_pattern(state)?)
} else {
None
};
Ok(crate::frontend::ast::StructPatternField {
name: field_name,
pattern,
})
}
fn handle_struct_field_separator(state: &mut ParserState) -> Result<bool> {
if matches!(state.tokens.peek(), Some((Token::Comma, _))) {
state.tokens.advance();
Ok(!matches!(state.tokens.peek(), Some((Token::RightBrace, _))))
} else if matches!(state.tokens.peek(), Some((Token::RightBrace, _))) {
Ok(false)
} else {
bail!("Expected comma or closing brace after struct pattern field")
}
}
pub(in crate::frontend::parser) fn parse_list_pattern(state: &mut ParserState) -> Result<Pattern> {
state.tokens.expect(&Token::LeftBracket)?;
let mut patterns = Vec::new();
while !matches!(state.tokens.peek(), Some((Token::RightBracket, _))) {
let pattern = parse_single_list_pattern_element(state)?;
patterns.push(pattern);
if !handle_pattern_separator(state, Token::RightBracket)? {
break;
}
}
state.tokens.expect(&Token::RightBracket)?;
Ok(Pattern::List(patterns))
}
fn parse_single_list_pattern_element(state: &mut ParserState) -> Result<Pattern> {
match state.tokens.peek() {
Some((Token::Identifier(name), _)) => {
let name = name.clone();
parse_identifier_pattern_with_default(state, name)
}
Some((Token::DotDot, _)) => parse_list_rest_pattern(state),
Some((Token::DotDotDot, _)) => parse_rest_pattern(state),
Some((Token::LeftParen, _)) => parse_tuple_pattern(state),
Some((Token::LeftBracket, _)) => parse_list_pattern(state),
Some((Token::Underscore, _)) => {
state.tokens.advance();
Ok(Pattern::Wildcard)
}
_ => bail!("Expected identifier, tuple, list, wildcard, or rest pattern in list pattern"),
}
}
fn parse_identifier_pattern_with_default(state: &mut ParserState, name: String) -> Result<Pattern> {
state.tokens.advance();
if matches!(state.tokens.peek(), Some((Token::Equal, _))) {
state.tokens.advance(); let default_expr = parse_expr_recursive(state)?;
Ok(Pattern::WithDefault {
pattern: Box::new(Pattern::Identifier(name)),
default: Box::new(default_expr),
})
} else {
Ok(Pattern::Identifier(name))
}
}
fn parse_rest_pattern(state: &mut ParserState) -> Result<Pattern> {
state.tokens.advance(); if let Some((Token::Identifier(name), _)) = state.tokens.peek() {
let name = name.clone();
state.tokens.advance();
Ok(Pattern::RestNamed(name))
} else {
Ok(Pattern::Rest)
}
}
fn handle_pattern_separator(state: &mut ParserState, end_token: Token) -> Result<bool> {
if matches!(state.tokens.peek(), Some((Token::Comma, _))) {
state.tokens.advance();
if let Some((token, _)) = state.tokens.peek() {
if *token == end_token {
return Ok(false);
}
}
Ok(true)
} else if let Some((token, _)) = state.tokens.peek() {
if *token != end_token {
let expected = match end_token {
Token::RightBracket => "',' or ']'",
Token::RightParen => "',' or ')'",
_ => "',' or closing delimiter",
};
bail!("Expected {expected} in pattern");
}
Ok(false)
} else {
bail!("Unexpected end of input in pattern")
}
}
pub(in crate::frontend::parser) fn parse_if_expression(state: &mut ParserState) -> Result<Expr> {
let start_span = state.tokens.expect(&Token::If)?;
if matches!(state.tokens.peek(), Some((Token::Let, _))) {
parse_if_let_expression(state, start_span)
} else {
parse_regular_if_expression(state, start_span)
}
}
fn parse_if_let_expression(state: &mut ParserState, start_span: Span) -> Result<Expr> {
state.tokens.advance(); let pattern = parse_match_pattern(state)
.map_err(|e| anyhow::anyhow!("Expected pattern after 'if let': {e}"))?;
state
.tokens
.expect(&Token::Equal)
.map_err(|e| anyhow::anyhow!("Expected '=' after pattern in if-let: {e}"))?;
let expr = Box::new(
parse_expr_recursive(state)
.map_err(|e| anyhow::anyhow!("Expected expression after '=' in if-let: {e}"))?,
);
let then_branch = Box::new(parse_expr_recursive(state).map_err(|e| {
anyhow::anyhow!("Expected body after if-let condition, typically {{ ... }}: {e}")
})?);
let else_branch = parse_else_branch(state)?;
Ok(Expr::new(
ExprKind::IfLet {
pattern,
expr,
then_branch,
else_branch,
},
start_span,
))
}
fn parse_regular_if_expression(state: &mut ParserState, start_span: Span) -> Result<Expr> {
let condition = Box::new(
parse_expr_recursive(state)
.map_err(|e| anyhow::anyhow!("Expected condition after 'if': {e}"))?,
);
let then_branch = Box::new(parse_expr_recursive(state).map_err(|e| {
anyhow::anyhow!("Expected body after if condition, typically {{ ... }}: {e}")
})?);
let else_branch = parse_else_branch(state)?;
Ok(Expr::new(
ExprKind::If {
condition,
then_branch,
else_branch,
},
start_span,
))
}
fn parse_else_branch(state: &mut ParserState) -> Result<Option<Box<Expr>>> {
if matches!(state.tokens.peek(), Some((Token::Else, _))) {
state.tokens.advance(); if matches!(state.tokens.peek(), Some((Token::If, _))) {
Ok(Some(Box::new(parse_if_expression(state)?)))
} else {
Ok(Some(Box::new(parse_expr_recursive(state).map_err(
|e| anyhow::anyhow!("Expected body after 'else', typically {{ ... }}: {e}"),
)?)))
}
} else {
Ok(None)
}
}
pub(in crate::frontend::parser) fn parse_match_expression(state: &mut ParserState) -> Result<Expr> {
let start_span = state.tokens.expect(&Token::Match)?;
let expr = Box::new(
parse_expr_recursive(state)
.map_err(|e| anyhow::anyhow!("Expected expression after 'match': {e}"))?,
);
state
.tokens
.expect(&Token::LeftBrace)
.map_err(|_| anyhow::anyhow!("Expected '{{' after match expression"))?;
let arms = parse_match_arms(state)?;
state
.tokens
.expect(&Token::RightBrace)
.map_err(|_| anyhow::anyhow!("Expected '}}' after match arms"))?;
Ok(Expr::new(ExprKind::Match { expr, arms }, start_span))
}
fn parse_match_arms(state: &mut ParserState) -> Result<Vec<MatchArm>> {
let mut arms = Vec::new();
while !matches!(state.tokens.peek(), Some((Token::RightBrace, _)) | None) {
let arm = parse_single_match_arm(state)?;
arms.push(arm);
if matches!(state.tokens.peek(), Some((Token::Comma, _))) {
state.tokens.advance();
}
if matches!(state.tokens.peek(), Some((Token::RightBrace, _))) {
break;
}
}
if arms.is_empty() {
bail!("Match expression must have at least one arm");
}
Ok(arms)
}
fn parse_guard_expression(state: &mut ParserState) -> Result<Expr> {
let old_context = state.in_guard_context;
state.in_guard_context = true;
let expr = parse_expr_recursive(state);
state.in_guard_context = old_context;
let expr = expr?;
if !matches!(
state.tokens.peek(),
Some((Token::FatArrow | Token::Arrow, _))
) {
bail!("Guard expression did not stop at match arm delimiter => or ->");
}
Ok(expr)
}
fn parse_single_match_arm(state: &mut ParserState) -> Result<MatchArm> {
let start_span = state.tokens.peek().map(|(_, s)| *s).unwrap_or_default();
let pattern = parse_match_pattern(state)?;
let guard = if matches!(state.tokens.peek(), Some((Token::If, _))) {
state.tokens.advance(); Some(Box::new(parse_guard_expression(state)?))
} else {
None
};
if !matches!(
state.tokens.peek(),
Some((Token::FatArrow | Token::Arrow, _))
) {
bail!("Expected '=>' or '->' in match arm");
}
state.tokens.advance();
let body = Box::new(parse_expr_recursive(state)?);
let end_span = body.span;
Ok(MatchArm {
pattern,
guard,
body,
span: start_span.merge(end_span),
})
}
pub(in crate::frontend::parser) fn parse_match_pattern(state: &mut ParserState) -> Result<Pattern> {
if state.tokens.peek().is_none() {
bail!("Expected pattern in match arm");
}
let pattern = parse_single_pattern(state)?;
if matches!(state.tokens.peek(), Some((Token::Pipe, _))) {
parse_or_pattern(state, pattern)
} else {
Ok(pattern)
}
}
pub(in crate::frontend::parser) fn parse_single_pattern(
state: &mut ParserState,
) -> Result<Pattern> {
let Some((token, _span)) = state.tokens.peek() else {
bail!("Expected pattern");
};
match token {
Token::Underscore => parse_wildcard_pattern(state),
Token::Integer(_)
| Token::Float(_)
| Token::String(_)
| Token::RawString(_)
| Token::Char(_)
| Token::Bool(_)
| Token::Atom(_) => parse_literal_pattern(state),
Token::Some | Token::None => parse_option_pattern(state),
Token::Ok | Token::Err => parse_result_pattern(state),
Token::Identifier(_) | Token::Result | Token::Var => {
parse_identifier_or_constructor_pattern(state)
}
Token::LeftParen => parse_match_tuple_pattern(state),
Token::LeftBracket => parse_match_list_pattern(state),
Token::LeftBrace => parse_struct_pattern(state),
_ => bail!("Unexpected token in pattern: {token:?}"),
}
}
fn parse_wildcard_pattern(state: &mut ParserState) -> Result<Pattern> {
state.tokens.advance();
Ok(Pattern::Wildcard)
}
fn parse_literal_pattern(state: &mut ParserState) -> Result<Pattern> {
let Some((token, _span)) = state.tokens.peek() else {
bail!("Expected literal pattern");
};
let token = token.clone(); let pattern = match token {
Token::Integer(val) => parse_integer_literal_pattern(state, val)?,
Token::Float(val) => parse_simple_literal_pattern(state, Literal::Float(val))?,
Token::String(s) => parse_simple_literal_pattern(state, Literal::String(s))?,
Token::RawString(s) => parse_simple_literal_pattern(state, Literal::String(s))?,
Token::Char(c) => parse_char_literal_pattern(state, c)?,
Token::Byte(b) => parse_simple_literal_pattern(state, Literal::Byte(b))?,
Token::Bool(b) => parse_simple_literal_pattern(state, Literal::Bool(b))?,
Token::Atom(s) => parse_simple_literal_pattern(state, Literal::Atom(s))?,
_ => bail!("Expected literal pattern, got: {token:?}"),
};
Ok(pattern)
}
fn parse_integer_literal_pattern(state: &mut ParserState, val: String) -> Result<Pattern> {
state.tokens.advance();
let (num_part, type_suffix) = if let Some(pos) = val.find(|c: char| c.is_alphabetic()) {
(&val[..pos], Some(val[pos..].to_string()))
} else {
(val.as_str(), None)
};
let parsed_val = num_part
.parse::<i64>()
.map_err(|_| anyhow::anyhow!("Invalid integer literal: {num_part}"))?;
match state.tokens.peek() {
Some((Token::DotDot, _)) => parse_integer_range_pattern(state, parsed_val, false),
Some((Token::DotDotEqual, _)) => parse_integer_range_pattern(state, parsed_val, true),
_ => Ok(Pattern::Literal(Literal::Integer(parsed_val, type_suffix))),
}
}
fn parse_integer_range_pattern(
state: &mut ParserState,
start_val: i64,
inclusive: bool,
) -> Result<Pattern> {
state.tokens.advance(); if let Some((Token::Integer(end_val_str), _)) = state.tokens.peek() {
let end_val_str = end_val_str.clone();
state.tokens.advance();
let (num_part, _type_suffix) =
if let Some(pos) = end_val_str.find(|c: char| c.is_alphabetic()) {
(&end_val_str[..pos], Some(end_val_str[pos..].to_string()))
} else {
(end_val_str.as_str(), None)
};
let end_val = num_part
.parse::<i64>()
.map_err(|_| anyhow::anyhow!("Invalid integer literal: {num_part}"))?;
Ok(Pattern::Range {
start: Box::new(Pattern::Literal(Literal::Integer(start_val, None))),
end: Box::new(Pattern::Literal(Literal::Integer(end_val, None))),
inclusive,
})
} else {
bail!("Expected integer after range operator");
}
}
fn parse_char_literal_pattern(state: &mut ParserState, val: char) -> Result<Pattern> {
state.tokens.advance();
match state.tokens.peek() {
Some((Token::DotDot, _)) => parse_char_range_pattern(state, val, false),
Some((Token::DotDotEqual, _)) => parse_char_range_pattern(state, val, true),
_ => Ok(Pattern::Literal(Literal::Char(val))),
}
}
fn parse_char_range_pattern(
state: &mut ParserState,
start_val: char,
inclusive: bool,
) -> Result<Pattern> {
state.tokens.advance(); if let Some((Token::Char(end_val), _)) = state.tokens.peek() {
let end_val = *end_val;
state.tokens.advance();
Ok(Pattern::Range {
start: Box::new(Pattern::Literal(Literal::Char(start_val))),
end: Box::new(Pattern::Literal(Literal::Char(end_val))),
inclusive,
})
} else {
bail!("Expected char after range operator");
}
}
fn parse_simple_literal_pattern(state: &mut ParserState, literal: Literal) -> Result<Pattern> {
state.tokens.advance();
Ok(Pattern::Literal(literal))
}
fn parse_option_pattern(state: &mut ParserState) -> Result<Pattern> {
let Some((token, _span)) = state.tokens.peek() else {
bail!("Expected Option pattern");
};
match token {
Token::Some => {
state.tokens.advance();
if matches!(state.tokens.peek(), Some((Token::LeftParen, _))) {
parse_constructor_pattern(state, "Some".to_string())
} else {
Ok(Pattern::Identifier("Some".to_string()))
}
}
Token::None => {
state.tokens.advance();
if matches!(state.tokens.peek(), Some((Token::LeftParen, _))) {
parse_constructor_pattern(state, "None".to_string())
} else {
Ok(Pattern::Identifier("None".to_string()))
}
}
_ => bail!("Expected Some or None pattern"),
}
}
fn parse_result_pattern(state: &mut ParserState) -> Result<Pattern> {
let Some((token, _span)) = state.tokens.peek() else {
bail!("Expected Result pattern");
};
match token {
Token::Ok => {
state.tokens.advance();
if matches!(state.tokens.peek(), Some((Token::LeftParen, _))) {
parse_constructor_pattern(state, "Ok".to_string())
} else {
Ok(Pattern::Identifier("Ok".to_string()))
}
}
Token::Err => {
state.tokens.advance();
if matches!(state.tokens.peek(), Some((Token::LeftParen, _))) {
parse_constructor_pattern(state, "Err".to_string())
} else {
Ok(Pattern::Identifier("Err".to_string()))
}
}
_ => bail!("Expected Ok or Err pattern"),
}
}
fn parse_identifier_or_constructor_pattern(state: &mut ParserState) -> Result<Pattern> {
let name = match state.tokens.peek() {
Some((Token::Identifier(n), _)) => n.clone(),
Some((Token::Result, _)) => "Result".to_string(),
Some((Token::Var, _)) => "var".to_string(),
_ => bail!("Expected identifier pattern"),
};
state.tokens.advance();
if matches!(state.tokens.peek(), Some((Token::At, _))) {
state.tokens.advance();
let inner_pattern = parse_single_pattern(state)?;
return Ok(Pattern::AtBinding {
name,
pattern: Box::new(inner_pattern),
});
}
if matches!(state.tokens.peek(), Some((Token::ColonColon, _))) {
let full_path = super::identifiers::parse_module_path_segments(state, name)?;
return if matches!(state.tokens.peek(), Some((Token::LeftBrace, _))) {
parse_struct_pattern_with_name(state, full_path)
} else if matches!(state.tokens.peek(), Some((Token::LeftParen, _))) {
parse_constructor_pattern(state, full_path)
} else {
let path_segments: Vec<String> =
full_path.split("::").map(ToString::to_string).collect();
Ok(Pattern::QualifiedName(path_segments))
};
}
if matches!(state.tokens.peek(), Some((Token::LeftBrace, _))) {
parse_struct_pattern_with_name(state, name)
}
else if matches!(state.tokens.peek(), Some((Token::LeftParen, _))) {
parse_constructor_pattern(state, name)
} else {
Ok(Pattern::Identifier(name))
}
}
fn parse_match_tuple_pattern(state: &mut ParserState) -> Result<Pattern> {
state.tokens.expect(&Token::LeftParen)?;
if matches!(state.tokens.peek(), Some((Token::RightParen, _))) {
state.tokens.advance();
return Ok(Pattern::Tuple(vec![]));
}
let mut patterns = vec![parse_match_pattern(state)?];
while matches!(state.tokens.peek(), Some((Token::Comma, _))) {
state.tokens.advance(); if matches!(state.tokens.peek(), Some((Token::RightParen, _))) {
break; }
patterns.push(parse_match_pattern(state)?);
}
state.tokens.expect(&Token::RightParen)?;
Ok(Pattern::Tuple(patterns))
}
fn parse_list_rest_pattern(state: &mut ParserState) -> Result<Pattern> {
state.tokens.advance(); if let Some((Token::Identifier(name), _)) = state.tokens.peek() {
let name = name.clone();
state.tokens.advance();
Ok(Pattern::RestNamed(name))
} else {
Ok(Pattern::Rest)
}
}
fn parse_list_pattern_element(state: &mut ParserState) -> Result<Pattern> {
if matches!(state.tokens.peek(), Some((Token::DotDot, _))) {
parse_list_rest_pattern(state)
} else if matches!(state.tokens.peek(), Some((Token::DotDotDot, _))) {
parse_rest_pattern(state)
} else {
parse_match_pattern(state)
}
}
fn parse_match_list_pattern(state: &mut ParserState) -> Result<Pattern> {
state.tokens.expect(&Token::LeftBracket)?;
if matches!(state.tokens.peek(), Some((Token::RightBracket, _))) {
state.tokens.advance();
return Ok(Pattern::List(vec![]));
}
let mut patterns = vec![];
loop {
patterns.push(parse_list_pattern_element(state)?);
if matches!(state.tokens.peek(), Some((Token::Comma, _))) {
state.tokens.advance();
if matches!(state.tokens.peek(), Some((Token::RightBracket, _))) {
break; }
} else {
break;
}
}
state.tokens.expect(&Token::RightBracket)?;
Ok(Pattern::List(patterns))
}
fn parse_constructor_pattern(state: &mut ParserState, name: String) -> Result<Pattern> {
state.tokens.expect(&Token::LeftParen)?;
let patterns = parse_constructor_arguments(state)?;
state.tokens.expect(&Token::RightParen)?;
create_constructor_pattern(name, patterns)
}
fn parse_constructor_arguments(state: &mut ParserState) -> Result<Vec<Pattern>> {
if matches!(state.tokens.peek(), Some((Token::RightParen, _))) {
return Ok(vec![]);
}
let mut patterns = vec![parse_match_pattern(state)?];
while matches!(state.tokens.peek(), Some((Token::Comma, _))) {
state.tokens.advance(); if matches!(state.tokens.peek(), Some((Token::RightParen, _))) {
break; }
patterns.push(parse_match_pattern(state)?);
}
Ok(patterns)
}
fn create_constructor_pattern(name: String, patterns: Vec<Pattern>) -> Result<Pattern> {
match (name.as_str(), patterns.len()) {
("Ok", 1) => {
Ok(Pattern::Ok(Box::new(patterns.into_iter().next().expect(
"patterns.len() == 1, so next() must return Some",
))))
}
("Err", 1) => {
Ok(Pattern::Err(Box::new(patterns.into_iter().next().expect(
"patterns.len() == 1, so next() must return Some",
))))
}
("Some", 1) => {
Ok(Pattern::Some(Box::new(patterns.into_iter().next().expect(
"patterns.len() == 1, so next() must return Some",
))))
}
("None", 0) => {
Ok(Pattern::None)
}
(name, 0) => {
if name.contains("::") {
let path: Vec<String> = name.split("::").map(ToString::to_string).collect();
Ok(Pattern::QualifiedName(path))
} else {
Ok(Pattern::Identifier(name.to_string()))
}
}
(name, _) => {
let path: Vec<String> = name.split("::").map(ToString::to_string).collect();
Ok(Pattern::TupleVariant { path, patterns })
}
}
}
fn parse_or_pattern(state: &mut ParserState, first: Pattern) -> Result<Pattern> {
let mut patterns = vec![first];
while matches!(state.tokens.peek(), Some((Token::Pipe, _))) {
state.tokens.advance(); let next = parse_single_pattern(state)?;
patterns.push(next);
}
if patterns.len() == 1 {
Ok(patterns
.into_iter()
.next()
.expect("patterns.len() == 1, so next() must return Some"))
} else {
Ok(Pattern::Or(patterns))
}
}
#[cfg(test)]
mod tests {
use crate::frontend::parser::Parser;
#[test]
fn test_identifier_pattern() {
let code = "let x = 42";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Identifier pattern should parse");
}
#[test]
fn test_tuple_pattern() {
let code = "let (x, y, z) = (1, 2, 3)";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Tuple pattern should parse");
}
#[test]
fn test_list_pattern_with_rest() {
let code = "let [first, ...rest] = [1, 2, 3, 4]";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "List pattern with rest should parse");
}
#[test]
fn test_struct_pattern() {
let code = "let Point { x, y } = point";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Struct pattern should parse");
}
#[test]
fn test_some_pattern() {
let code = "let Some(x) = maybe_value";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Some pattern should parse");
}
#[test]
fn test_ok_pattern() {
let code = "let Ok(val) = result";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Ok pattern should parse");
}
#[test]
fn test_err_pattern() {
let code = "let Err(e) = result";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Err pattern should parse");
}
#[test]
fn test_none_pattern() {
let code = "let None = maybe_value";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "None pattern should parse");
}
#[test]
fn test_wildcard_pattern() {
let code = "let _ = value";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Wildcard pattern should parse");
}
#[test]
fn test_literal_pattern() {
let code = "match x { 42 => true, _ => false }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Literal pattern in match should parse");
}
#[test]
fn test_range_pattern() {
let code = "match x { 1..10 => \"low\", _ => \"high\" }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Range pattern should parse");
}
#[test]
fn test_or_pattern() {
let code = "match x { Some(1) | Some(2) => true, _ => false }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Or pattern should parse");
}
#[test]
fn test_parser_082_atom_pattern_simple() {
let code = "match x { :ok => true, :error => false }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Atom pattern :ok should parse");
}
#[test]
fn test_parser_082_atom_pattern_with_wildcard() {
let code = "match status { :ok => handle_ok(), :error => handle_error(), _ => default() }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Atom patterns with wildcard should parse");
}
#[test]
fn test_parser_082_atom_pattern_or() {
let code = "match x { :ok | :success => true, _ => false }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Or patterns with atoms should parse");
}
#[test]
fn test_parser_082_atom_pattern_in_tuple() {
let code = "match pair { (:ok, value) => value, (:error, msg) => panic(msg) }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Atom in tuple pattern should parse");
}
#[test]
fn test_nested_tuple_pattern() {
let code = "let ((a, b), c) = ((1, 2), 3)";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Nested tuple pattern should parse");
}
#[test]
fn test_list_pattern_without_rest() {
let code = "let [a, b, c] = [1, 2, 3]";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "List pattern without rest should parse");
}
#[test]
fn test_tuple_variant_pattern() {
let code = "match x { Point(a, b) => a + b }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Tuple variant pattern should parse");
}
#[test]
fn test_struct_pattern_with_rest() {
let code = "let Point { x, .. } = point";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Struct pattern with rest should parse");
}
#[test]
fn test_match_with_guard() {
let code = "match x { n if n > 0 => true, _ => false }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Match with guard should parse");
}
#[test]
fn test_match_inclusive_range() {
let code = "match x { 1..=10 => \"in range\", _ => \"out\" }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Inclusive range pattern should parse");
}
#[test]
fn test_if_let_expression() {
let code = "if let Some(x) = maybe { x } else { 0 }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "If-let expression should parse");
}
#[test]
fn test_string_literal_pattern() {
let code = r#"match s { "hello" => true, _ => false }"#;
let result = Parser::new(code).parse();
assert!(result.is_ok(), "String literal pattern should parse");
}
#[test]
fn test_bool_literal_pattern() {
let code = "match b { true => 1, false => 0 }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Bool literal pattern should parse");
}
#[test]
fn test_float_literal_pattern() {
let code = "match f { 3.14 => \"pi\", _ => \"other\" }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Float literal pattern should parse");
}
#[test]
fn test_multiple_or_patterns() {
let code = "match x { 1 | 2 | 3 => true, _ => false }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Multiple or patterns should parse");
}
#[test]
fn test_char_literal_pattern() {
let code = "match c { 'a' => 1, 'b' => 2, _ => 0 }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Char literal pattern should parse");
}
#[test]
fn test_char_range_pattern() {
let code = "match c { 'a'..'z' => true, _ => false }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Char range pattern should parse");
}
#[test]
fn test_match_with_wildcard_only() {
let code = "match x { _ => 0 }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Match with wildcard arm should parse");
}
#[test]
fn test_match_multiple_arms() {
let code = "match n { 0 => \"zero\", 1 => \"one\", 2 => \"two\", _ => \"many\" }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Match with multiple arms should parse");
}
#[test]
fn test_let_with_type_annotation() {
let code = "let x: i32 = 42";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Let with type annotation should parse");
}
#[test]
fn test_mutable_pattern() {
let code = "let mut x = 42";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Mutable let should parse");
}
#[test]
fn test_var_declaration() {
let code = "var x = 42";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Var declaration should parse");
}
#[test]
fn test_constructor_pattern_no_args() {
let code = "match x { Unit => 0 }";
let result = Parser::new(code).parse();
assert!(
result.is_ok(),
"Constructor pattern without args should parse"
);
}
#[test]
fn test_constructor_pattern_with_args() {
let code = "match x { Pair(a, b) => a + b }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Constructor pattern with args should parse");
}
#[test]
fn test_nested_struct_pattern() {
let code = "let Line { start: Point { x, y }, end } = line";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Nested struct pattern should parse");
}
#[test]
fn test_struct_field_rename() {
let code = "let Point { x: new_x, y: new_y } = point";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Struct field rename should parse");
}
#[test]
fn test_complex_match_expression() {
let code = r#"match result {
Ok(value) if value > 0 => value * 2,
Ok(0) => 0,
Err(e) => -1
}"#;
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Complex match expression should parse");
}
#[test]
fn test_if_let_with_else_if() {
let code = "if let Some(x) = a { x } else if let Some(y) = b { y } else { 0 }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Chained if-let should parse");
}
#[test]
fn test_let_else_clause() {
let code = "let Some(x) = maybe else { return 0 }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Let-else should parse");
}
#[test]
fn test_pattern_with_default_value() {
let code = "fun foo(x = 10) { x }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Pattern with default value should parse");
}
#[test]
fn test_rest_pattern_only() {
let code = "let [...all] = [1, 2, 3]";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Rest pattern only should parse");
}
#[test]
fn test_large_integer_pattern() {
let code = "match x { 100 => \"hundred\", _ => \"other\" }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Large integer pattern should parse");
}
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_tuple_pattern_empty() {
let result = parse("let () = ()");
assert!(result.is_ok(), "Empty tuple pattern should parse");
}
#[test]
fn test_tuple_pattern_single() {
let result = parse("let (x,) = (1,)");
assert!(result.is_ok(), "Single element tuple should parse");
}
#[test]
fn test_tuple_pattern_four_elements() {
let result = parse("let (a, b, c, d) = (1, 2, 3, 4)");
assert!(result.is_ok(), "Four element tuple should parse");
}
#[test]
fn test_tuple_pattern_with_wildcards() {
let result = parse("let (x, _, z) = (1, 2, 3)");
assert!(result.is_ok(), "Tuple with wildcards should parse");
}
#[test]
fn test_tuple_pattern_nested_three_levels() {
let result = parse("let (((a, b), c), d) = (((1, 2), 3), 4)");
assert!(result.is_ok(), "Deeply nested tuple should parse");
}
#[test]
fn test_tuple_pattern_with_mut_element() {
let result = parse("let (mut x, y) = (1, 2)");
assert!(result.is_ok(), "Tuple with mut element should parse");
}
#[test]
fn test_list_pattern_empty() {
let result = parse("let [] = []");
assert!(result.is_ok(), "Empty list pattern should parse");
}
#[test]
fn test_list_pattern_single_element() {
let result = parse("let [x] = [1]");
assert!(result.is_ok(), "Single element list should parse");
}
#[test]
fn test_list_pattern_with_trailing_rest() {
let result = parse("let [first, second, ...rest] = arr");
assert!(result.is_ok(), "List with trailing rest should parse");
}
#[test]
fn test_list_pattern_with_two_dot_rest() {
let result = parse("let [head, ..tail] = arr");
assert!(result.is_ok(), "List with two-dot rest should parse");
}
#[test]
fn test_list_pattern_rest_only() {
let result = parse("let [..all] = arr");
assert!(result.is_ok(), "List with only rest should parse");
}
#[test]
fn test_list_pattern_with_wildcards() {
let result = parse("let [first, _, third] = arr");
assert!(result.is_ok(), "List with wildcards should parse");
}
#[test]
fn test_struct_pattern_single_field() {
let result = parse("let Point { x } = point");
assert!(result.is_ok(), "Struct with single field should parse");
}
#[test]
fn test_struct_pattern_three_fields() {
let result = parse("let Color { r, g, b } = color");
assert!(result.is_ok(), "Struct with three fields should parse");
}
#[test]
fn test_struct_pattern_rest_only() {
let result = parse("let Point { .. } = point");
assert!(result.is_ok(), "Struct with only rest should parse");
}
#[test]
fn test_struct_pattern_field_with_nested() {
let result = parse("let Line { start: Point { x, y }, end } = line");
assert!(result.is_ok(), "Struct with nested pattern should parse");
}
#[test]
fn test_struct_pattern_anonymous() {
let result = parse("let { name, age } = person");
assert!(result.is_ok(), "Anonymous struct pattern should parse");
}
#[test]
fn test_struct_pattern_trailing_comma() {
let result = parse("let Point { x, y, } = point");
assert!(result.is_ok(), "Struct with trailing comma should parse");
}
#[test]
fn test_match_with_block_body() {
let result = parse("match x { 1 => { let a = 1; a + 1 }, _ => 0 }");
assert!(result.is_ok(), "Match with block body should parse");
}
#[test]
fn test_match_arrow_syntax() {
let result = parse("match x { 1 -> true, _ -> false }");
assert!(result.is_ok(), "Match with arrow syntax should parse");
}
#[test]
fn test_match_five_arms() {
let result = parse(
"match x { 0 => \"zero\", 1 => \"one\", 2 => \"two\", 3 => \"three\", _ => \"many\" }",
);
assert!(result.is_ok(), "Match with five arms should parse");
}
#[test]
fn test_match_guard_with_function_call() {
let result = parse("match x { n if is_valid(n) => true, _ => false }");
assert!(
result.is_ok(),
"Match with function call guard should parse"
);
}
#[test]
fn test_match_guard_with_comparison() {
let result = parse("match x { n if n >= 0 && n < 100 => true, _ => false }");
assert!(result.is_ok(), "Match with comparison guard should parse");
}
#[test]
fn test_match_nested() {
let result =
parse("match x { Some(y) => match y { 1 => true, _ => false }, None => false }");
assert!(result.is_ok(), "Nested match should parse");
}
#[test]
fn test_match_with_trailing_comma() {
let result = parse("match x { 1 => true, 2 => false, }");
assert!(result.is_ok(), "Match with trailing comma should parse");
}
#[test]
fn test_if_let_without_else() {
let result = parse("if let Some(x) = opt { print(x) }");
assert!(result.is_ok(), "If-let without else should parse");
}
#[test]
fn test_if_let_nested_pattern() {
let result = parse("if let Some((a, b)) = opt { a + b } else { 0 }");
assert!(result.is_ok(), "If-let with nested pattern should parse");
}
#[test]
fn test_if_let_ok_pattern() {
let result = parse("if let Ok(val) = result { val } else { 0 }");
assert!(result.is_ok(), "If-let with Ok should parse");
}
#[test]
fn test_if_let_err_pattern() {
let result = parse("if let Err(e) = result { log(e) }");
assert!(result.is_ok(), "If-let with Err should parse");
}
#[test]
fn test_if_let_struct_pattern() {
let result = parse("if let Point { x, y } = point { x + y } else { 0 }");
assert!(result.is_ok(), "If-let with struct should parse");
}
#[test]
fn test_if_let_chain() {
let result = parse("if let Some(x) = a { x } else if let Some(y) = b { y } else { 0 }");
assert!(result.is_ok(), "If-let chain should parse");
}
#[test]
fn test_or_pattern_three_alternatives() {
let result = parse("match x { 1 | 2 | 3 => true, _ => false }");
assert!(result.is_ok(), "Three-way or should parse");
}
#[test]
fn test_or_pattern_with_unit_variants() {
let result = parse("match x { A | B => 1, _ => 0 }");
assert!(result.is_ok(), "Or with unit variants should parse");
}
#[test]
fn test_or_pattern_strings() {
let result = parse(r#"match s { "yes" | "y" | "Y" => true, _ => false }"#);
assert!(result.is_ok(), "Or with strings should parse");
}
#[test]
fn test_or_pattern_with_binding() {
let result = parse("match x { Some(n) | Ok(n) => n, _ => 0 }");
assert!(result.is_ok(), "Or with bindings should parse");
}
#[test]
fn test_range_pattern_exclusive() {
let result = parse("match x { 0..10 => true, _ => false }");
assert!(result.is_ok(), "Exclusive range should parse");
}
#[test]
fn test_range_pattern_inclusive() {
let result = parse("match x { 0..=10 => true, _ => false }");
assert!(result.is_ok(), "Inclusive range should parse");
}
#[test]
fn test_range_pattern_char_exclusive() {
let result = parse("match c { 'a'..'z' => true, _ => false }");
assert!(result.is_ok(), "Char exclusive range should parse");
}
#[test]
fn test_range_pattern_char_inclusive() {
let result = parse("match c { 'a'..='z' => true, _ => false }");
assert!(result.is_ok(), "Char inclusive range should parse");
}
#[test]
fn test_at_binding_simple() {
let result = parse("match x { n @ 1..10 => n, _ => 0 }");
assert!(result.is_ok(), "At binding with range should parse");
}
#[test]
fn test_at_binding_with_pattern() {
let result = parse("match opt { val @ Some(_) => val, None => None }");
assert!(result.is_ok(), "At binding with Some should parse");
}
#[test]
fn test_qualified_enum_variant() {
let result = parse("match x { Color::Red => 1, Color::Green => 2, _ => 0 }");
assert!(result.is_ok(), "Qualified enum variant should parse");
}
#[test]
fn test_qualified_tuple_variant() {
let result = parse("match x { Color::RGB(r, g, b) => r, _ => 0 }");
assert!(result.is_ok(), "Qualified tuple variant should parse");
}
#[test]
fn test_qualified_struct_variant() {
let result = parse("match x { Shape::Circle { radius } => radius, _ => 0.0 }");
assert!(result.is_ok(), "Qualified struct variant should parse");
}
#[test]
fn test_let_else_with_panic() {
let result = parse("let Some(x) = opt else { panic(\"no value\") }");
assert!(result.is_ok(), "Let-else with panic should parse");
}
#[test]
fn test_let_else_with_ok() {
let result = parse("let Ok(val) = result else { return Err(e) }");
assert!(result.is_ok(), "Let-else with Ok should parse");
}
#[test]
fn test_let_else_complex_block() {
let result = parse(
r#"let Some(x) = opt else {
log("error")
return 0
}"#,
);
assert!(result.is_ok(), "Let-else with complex block should parse");
}
#[test]
fn test_pattern_in_function_param() {
let result = parse("fun foo((x, y)) { x + y }");
assert!(
result.is_ok(),
"Tuple pattern in function param should parse"
);
}
#[test]
fn test_pattern_with_type_keyword_field() {
let result = parse("let { type } = config");
let _ = result;
}
#[test]
fn test_constructor_empty_args() {
let result = parse("match x { Unit() => true, _ => false }");
assert!(result.is_ok(), "Empty constructor args should parse");
}
#[test]
fn test_constructor_three_args() {
let result = parse("match x { Triple(a, b, c) => a + b + c }");
assert!(result.is_ok(), "Constructor with three args should parse");
}
#[test]
fn test_match_complex_guard() {
let result = parse("match (x, y) { (a, b) if a > 0 && b > 0 => true, _ => false }");
assert!(result.is_ok(), "Match with complex guard should parse");
}
#[test]
fn test_pattern_result_identifier() {
let result = parse("let Result = compute()");
assert!(result.is_ok(), "'Result' as identifier should parse");
}
#[test]
fn test_pattern_var_identifier() {
let result = parse("let var = value");
let _ = result;
}
#[test]
fn test_empty_tuple_in_match() {
let result = parse("match x { () => true }");
assert!(result.is_ok(), "Empty tuple in match should parse");
}
#[test]
fn test_empty_list_in_match() {
let result = parse("match arr { [] => true, _ => false }");
assert!(result.is_ok(), "Empty list in match should parse");
}
#[cfg(test)]
mod property_tests {
use super::*;
use proptest::prelude::*;
proptest! {
#[test]
#[ignore = "Property tests run with --ignored flag"] fn prop_identifier_patterns_parse(name in "[a-z][a-z0-9_]*") {
let code = format!("let {name} = 42");
let result = Parser::new(&code).parse();
prop_assert!(result.is_ok());
}
#[test]
#[ignore = "Property tests run with --ignored flag"]
fn prop_tuple_patterns_parse(a in "[a-z]+", b in "[a-z]+") {
let code = format!("let ({a}, {b}) = (1, 2)");
let result = Parser::new(&code).parse();
prop_assert!(result.is_ok());
}
#[test]
#[ignore = "Property tests run with --ignored flag"]
fn prop_list_patterns_parse(name in "[a-z]+") {
let code = format!("let [{name}, ...rest] = [1, 2, 3]");
let result = Parser::new(&code).parse();
prop_assert!(result.is_ok());
}
#[test]
#[ignore = "Property tests run with --ignored flag"]
fn prop_some_patterns_parse(inner in "[a-z]+") {
let code = format!("let Some({inner}) = value");
let result = Parser::new(&code).parse();
prop_assert!(result.is_ok());
}
#[test]
#[ignore = "Property tests run with --ignored flag"]
fn prop_wildcard_always_parses(_seed in any::<u32>()) {
let code = "let _ = 42";
let result = Parser::new(code).parse();
prop_assert!(result.is_ok());
}
#[test]
#[ignore = "Property tests run with --ignored flag"]
fn prop_literal_patterns_parse(n in 0i32..1000) {
let code = format!("match x {{ {n} => true, _ => false }}");
let result = Parser::new(&code).parse();
prop_assert!(result.is_ok());
}
#[test]
#[ignore = "Property tests run with --ignored flag"]
fn prop_struct_patterns_parse(field in "[a-z]+") {
let code = format!("let Point {{ {field} }} = p");
let result = Parser::new(&code).parse();
prop_assert!(result.is_ok());
}
}
}
}