use crate::frontend::ast::{Expr, ExprKind, Span};
use crate::frontend::lexer::Token;
use crate::frontend::parser::{bail, parse_expr_recursive, ParserState, Result};
pub(in crate::frontend::parser) fn parse_increment_token(
state: &mut ParserState,
span: Span,
) -> Result<Expr> {
state.tokens.advance();
let variable = parse_expr_recursive(state)?;
Ok(Expr::new(
ExprKind::PreIncrement {
target: Box::new(variable),
},
span,
))
}
pub(in crate::frontend::parser) fn parse_decrement_token(
state: &mut ParserState,
span: Span,
) -> Result<Expr> {
state.tokens.advance();
let variable = parse_expr_recursive(state)?;
Ok(Expr::new(
ExprKind::PreDecrement {
target: Box::new(variable),
},
span,
))
}
pub(in crate::frontend::parser) fn parse_constructor_token(
state: &mut ParserState,
token: Token,
span: Span,
) -> Result<Expr> {
let constructor_name = match token {
Token::Some => "Some",
Token::None => "None",
Token::Ok => "Ok",
Token::Err => "Err",
Token::Result => "Result",
Token::Option => "Option",
_ => bail!("Expected constructor token, got: {token:?}"),
};
state.tokens.advance();
if matches!(state.tokens.peek(), Some((Token::ColonColon, _))) {
state.tokens.advance(); if let Some((next_token, _)) = state.tokens.peek() {
let variant_name = match next_token.clone() {
Token::Some => "Some".to_string(),
Token::None => "None".to_string(),
Token::Ok => "Ok".to_string(),
Token::Err => "Err".to_string(),
Token::Identifier(name) => name,
_ => bail!("Expected variant name after '::'"),
};
state.tokens.advance();
let qualified_name = format!("{constructor_name}::{variant_name}");
return Ok(Expr::new(ExprKind::Identifier(qualified_name), span));
}
bail!("Expected variant name after '::'");
}
Ok(Expr::new(
ExprKind::Identifier(constructor_name.to_string()),
span,
))
}
#[cfg(test)]
mod tests {
use crate::frontend::parser::Parser;
#[test]
fn test_pre_increment() {
let code = "++x";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Pre-increment should parse");
}
#[test]
fn test_pre_decrement() {
let code = "--x";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Pre-decrement should parse");
}
#[test]
fn test_result_type_constructor() {
let code = "Result";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Result type should parse");
}
#[test]
fn test_option_type_constructor() {
let code = "Option";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Option type should parse");
}
#[test]
fn test_ok_variant() {
let code = "Ok";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Ok variant should parse");
}
#[test]
fn test_err_variant() {
let code = "Err";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Err variant should parse");
}
#[test]
fn test_qualified_constructor() {
let code = "Option::Some";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Qualified constructor should parse");
}
#[test]
fn test_some_variant() {
let code = "let x = Some(1)";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Some variant should parse");
}
#[test]
fn test_none_variant() {
let code = "None";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "None variant should parse");
}
#[test]
fn test_result_ok_qualified() {
let code = "Result::Ok";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Result::Ok should parse");
}
#[test]
fn test_result_err_qualified() {
let code = "Result::Err";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Result::Err should parse");
}
#[test]
fn test_option_none_qualified() {
let code = "Option::None";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Option::None should parse");
}
#[test]
fn test_pre_increment_array() {
let code = "++arr[0]";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Pre-increment array should parse");
}
#[test]
fn test_pre_decrement_array() {
let code = "--arr[0]";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Pre-decrement array should parse");
}
#[test]
fn test_pre_increment_in_expr() {
let code = "let x = ++y";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Pre-increment in let should parse");
}
#[test]
fn test_pre_decrement_in_expr() {
let code = "let x = --y";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Pre-decrement in let should parse");
}
#[test]
fn test_some_call() {
let code = "Some(42)";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Some(42) should parse");
}
#[test]
fn test_none_in_expr() {
let code = "let x = None";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "None in let should parse");
}
#[test]
fn test_ok_call() {
let code = "Ok(value)";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Ok(value) should parse");
}
#[test]
fn test_err_call() {
let code = "Err(message)";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Err(message) should parse");
}
#[test]
fn test_option_some_call() {
let code = "Option::Some(42)";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Option::Some(42) should parse");
}
#[test]
fn test_multiple_increments() {
let code = "{ ++x; ++y }";
let result = Parser::new(code).parse();
assert!(result.is_ok(), "Multiple increments should parse");
}
}
#[cfg(test)]
mod property_tests {
use crate::frontend::parser::Parser;
use proptest::prelude::*;
proptest! {
#[test]
fn test_increment_never_panics(var in "[a-z][a-z0-9_]{0,10}") {
let code = format!("++{var}");
let _ = Parser::new(&code).parse();
}
#[test]
fn test_decrement_never_panics(var in "[a-z][a-z0-9_]{0,10}") {
let code = format!("--{var}");
let _ = Parser::new(&code).parse();
}
#[test]
fn test_constructors_never_panic(constructor in "(Some|None|Ok|Err|Result|Option)") {
let _ = Parser::new(&constructor).parse();
}
#[test]
fn test_qualified_constructors_never_panic(
container in "(Option|Result)",
variant in "(Some|None|Ok|Err)"
) {
let code = format!("{container}::{variant}");
let _ = Parser::new(&code).parse();
}
}
}