use super::{bail, expressions, functions, Expr, ExprKind, ParserState, Result, Span, Token};
use crate::frontend::ast::{DataFrameColumn, Literal, ObjectField};
pub fn parse_block(state: &mut ParserState) -> Result<Expr> {
let start_span = state.tokens.advance().expect("checked by parser logic").1;
if let Ok(comprehension) = try_parse_comprehension(state, start_span) {
return Ok(comprehension);
}
if is_object_literal(state) {
return parse_object_literal_body(state, start_span);
}
if let Ok(block_result) = try_parse_block_expressions(state, start_span) {
return Ok(block_result);
}
if let Ok(set_literal) = try_parse_set_literal(state, start_span) {
return Ok(set_literal);
}
skip_comments(state);
state.tokens.expect(&Token::RightBrace)?;
Ok(create_block_result(Vec::new(), start_span))
}
fn try_parse_block_expressions(state: &mut ParserState, start_span: Span) -> Result<Expr> {
let saved_position = state.tokens.position();
if let Ok(exprs) = parse_block_expressions(state, start_span) {
skip_comments(state);
if matches!(state.tokens.peek(), Some((Token::RightBrace, _))) {
state.tokens.advance(); Ok(create_block_result(exprs, start_span))
} else {
state.tokens.set_position(saved_position);
bail!("Not a valid block - missing closing brace")
}
} else {
state.tokens.set_position(saved_position);
bail!("Not a valid block expression")
}
}
fn skip_comments(state: &mut ParserState) {
while matches!(
state.tokens.peek(),
Some((
Token::LineComment(_)
| Token::BlockComment(_)
| Token::DocComment(_)
| Token::HashComment(_),
_
))
) {
state.tokens.advance();
}
}
pub(in crate::frontend::parser) fn parse_block_expressions(
state: &mut ParserState,
start_span: Span,
) -> Result<Vec<Expr>> {
let mut exprs = Vec::new();
while !matches!(state.tokens.peek(), Some((Token::RightBrace, _))) {
skip_comments(state);
if matches!(state.tokens.peek(), Some((Token::RightBrace, _))) {
break;
}
let expr = parse_next_block_expression(state, start_span)?;
exprs.push(expr);
consume_optional_semicolon(state);
if matches!(state.tokens.peek(), Some((Token::RightBrace, _))) {
break;
}
}
Ok(exprs)
}
fn parse_next_block_expression(state: &mut ParserState, start_span: Span) -> Result<Expr> {
let _attributes = super::utils::parse_attributes(state)?;
let mut expr = if matches!(state.tokens.peek(), Some((Token::Let, _))) {
parse_potential_let_statement(state, start_span)?
} else {
super::parse_expr_recursive(state)?
};
match &mut expr.kind {
ExprKind::Function { .. } | ExprKind::Struct { .. } | ExprKind::Class { .. } => {
}
_ => {
}
}
Ok(expr)
}
fn parse_potential_let_statement(state: &mut ParserState, start_span: Span) -> Result<Expr> {
let saved_pos = state.tokens.position();
state.tokens.advance(); if let Some(let_info) = try_parse_let_binding(state)? {
if is_let_expression(state) {
state.tokens.set_position(saved_pos);
super::parse_expr_recursive(state)
} else {
create_let_statement_expression(state, let_info, start_span)
}
} else {
state.tokens.set_position(saved_pos);
super::parse_expr_recursive(state)
}
}
fn try_parse_let_binding(state: &mut ParserState) -> Result<Option<LetBindingInfo>> {
if let Some((Token::Identifier(name), _)) = state.tokens.peek() {
let name = name.clone();
state.tokens.advance();
if matches!(state.tokens.peek(), Some((Token::Equal, _))) {
state.tokens.advance(); let value = super::parse_expr_recursive(state)?;
return Ok(Some(LetBindingInfo { name, value }));
}
}
Ok(None)
}
fn is_let_expression(state: &mut ParserState) -> bool {
matches!(state.tokens.peek(), Some((Token::In, _)))
}
fn create_let_statement_expression(
state: &mut ParserState,
let_info: LetBindingInfo,
start_span: Span,
) -> Result<Expr> {
consume_optional_semicolon(state);
let body = parse_remaining_block_body(state, start_span)?;
Ok(Expr::new(
ExprKind::Let {
name: let_info.name,
type_annotation: None,
value: Box::new(let_info.value),
body: Box::new(body),
is_mutable: false,
else_block: None, },
start_span,
))
}
fn parse_remaining_block_body(state: &mut ParserState, start_span: Span) -> Result<Expr> {
let mut body_exprs = Vec::new();
while !matches!(state.tokens.peek(), Some((Token::RightBrace, _))) {
body_exprs.push(parse_next_block_expression(state, start_span)?);
consume_optional_semicolon(state);
if matches!(state.tokens.peek(), Some((Token::RightBrace, _))) {
break;
}
}
Ok(create_body_expression(body_exprs, start_span))
}
fn create_body_expression(body_exprs: Vec<Expr>, start_span: Span) -> Expr {
if body_exprs.is_empty() {
Expr::new(ExprKind::Literal(Literal::Unit), start_span)
} else if body_exprs.len() == 1 {
body_exprs.into_iter().next().expect("checked: len == 1")
} else {
Expr::new(ExprKind::Block(body_exprs), start_span)
}
}
fn create_block_result(exprs: Vec<Expr>, start_span: Span) -> Expr {
if exprs.is_empty() {
Expr::new(ExprKind::Literal(Literal::Unit), start_span)
} else {
Expr::new(ExprKind::Block(exprs), start_span)
}
}
fn consume_optional_semicolon(state: &mut ParserState) {
if matches!(state.tokens.peek(), Some((Token::Semicolon, _))) {
state.tokens.advance();
while matches!(
state.tokens.peek(),
Some((
Token::LineComment(_)
| Token::BlockComment(_)
| Token::DocComment(_)
| Token::HashComment(_),
_
))
) {
state.tokens.advance();
}
}
}
#[derive(Debug, Clone)]
struct LetBindingInfo {
name: String,
value: Expr,
}
fn is_object_literal(state: &mut ParserState) -> bool {
if is_empty_braces(state) {
return true;
}
if is_spread_operator(state) {
return true;
}
check_for_object_key_separator(state)
}
fn is_empty_braces(state: &mut ParserState) -> bool {
matches!(state.tokens.peek(), Some((Token::RightBrace, _)))
}
fn is_spread_operator(state: &mut ParserState) -> bool {
matches!(state.tokens.peek(), Some((Token::DotDotDot, _)))
}
fn check_for_object_key_separator(state: &mut ParserState) -> bool {
if let Some((token, _)) = state.tokens.peek() {
if can_be_object_key(token) {
let saved_pos = state.tokens.position();
state.tokens.advance();
let has_separator = matches!(
state.tokens.peek(),
Some((Token::Colon | Token::FatArrow, _))
);
let is_dict_comprehension = has_separator
&& matches!(state.tokens.peek(), Some((Token::Colon, _)))
&& lookahead_for_comprehension(state);
state.tokens.set_position(saved_pos);
return has_separator && !is_dict_comprehension;
}
}
false
}
fn can_be_object_key(token: &Token) -> bool {
matches!(
token,
Token::Identifier(_) | Token::String(_) | Token::RawString(_) | Token::Atom(_)
) || control_flow_token_to_key(token).is_some()
|| declaration_token_to_key(token).is_some()
|| type_token_to_key(token).is_some()
|| module_token_to_key(token).is_some()
|| async_error_token_to_key(token).is_some()
}
fn lookahead_for_comprehension(state: &mut ParserState) -> bool {
let saved_pos = state.tokens.position();
state.tokens.advance();
let found_for = scan_for_comprehension_keyword(state);
state.tokens.set_position(saved_pos);
found_for
}
fn scan_for_comprehension_keyword(state: &mut ParserState) -> bool {
let mut token_count = 0;
while token_count < 20 {
match state.tokens.peek() {
Some((Token::For, _)) => return true,
Some((Token::RightBrace, _)) => return false,
Some((Token::Comma, _)) => return false,
Some(_) => {
state.tokens.advance();
token_count += 1;
}
None => return false,
}
}
false
}
fn parse_object_key(state: &mut ParserState) -> Result<String> {
if let Some((token, _)) = state.tokens.peek() {
let key = token_to_object_key(token)?;
state.tokens.advance();
Ok(key)
} else {
bail!("Expected key in object literal")
}
}
fn control_flow_token_to_key(token: &Token) -> Option<String> {
match token {
Token::If => Some("if".to_string()),
Token::Else => Some("else".to_string()),
Token::For => Some("for".to_string()),
Token::While => Some("while".to_string()),
Token::Loop => Some("loop".to_string()),
Token::Match => Some("match".to_string()),
Token::Break => Some("break".to_string()),
Token::Continue => Some("continue".to_string()),
Token::Return => Some("return".to_string()),
_ => None,
}
}
fn declaration_token_to_key(token: &Token) -> Option<String> {
match token {
Token::Let => Some("let".to_string()),
Token::Var => Some("var".to_string()),
Token::Const => Some("const".to_string()),
Token::Static => Some("static".to_string()),
Token::Pub => Some("pub".to_string()),
Token::Mut => Some("mut".to_string()),
Token::Fun => Some("fun".to_string()),
Token::Fn => Some("fn".to_string()),
_ => None,
}
}
fn type_token_to_key(token: &Token) -> Option<String> {
match token {
Token::Type => Some("type".to_string()),
Token::Struct => Some("struct".to_string()),
Token::Enum => Some("enum".to_string()),
Token::Impl => Some("impl".to_string()),
Token::Trait => Some("trait".to_string()),
_ => None,
}
}
fn module_token_to_key(token: &Token) -> Option<String> {
match token {
Token::Module => Some("module".to_string()),
Token::Import => Some("import".to_string()),
Token::Export => Some("export".to_string()),
Token::Use => Some("use".to_string()),
Token::As => Some("as".to_string()),
Token::From => Some("from".to_string()),
Token::Self_ => Some("self".to_string()),
Token::Super => Some("super".to_string()),
Token::Crate => Some("crate".to_string()),
Token::In => Some("in".to_string()),
Token::Where => Some("where".to_string()),
_ => None,
}
}
fn async_error_token_to_key(token: &Token) -> Option<String> {
match token {
Token::Async => Some("async".to_string()),
Token::Await => Some("await".to_string()),
Token::Try => Some("try".to_string()),
Token::Catch => Some("catch".to_string()),
Token::Throw => Some("throw".to_string()),
_ => None,
}
}
fn token_to_object_key(token: &Token) -> Result<String> {
match token {
Token::Identifier(name) => Ok(name.clone()),
Token::String(s) | Token::RawString(s) => Ok(s.clone()),
Token::Atom(s) => Ok(format!(":{s}")),
_ => {
if let Some(key) = control_flow_token_to_key(token) {
return Ok(key);
}
if let Some(key) = declaration_token_to_key(token) {
return Ok(key);
}
if let Some(key) = type_token_to_key(token) {
return Ok(key);
}
if let Some(key) = module_token_to_key(token) {
return Ok(key);
}
if let Some(key) = async_error_token_to_key(token) {
return Ok(key);
}
bail!("Expected identifier or string key in object literal")
}
}
}
fn parse_object_literal_body(state: &mut ParserState, start_span: Span) -> Result<Expr> {
let mut fields = Vec::new();
while !matches!(state.tokens.peek(), Some((Token::RightBrace, _))) {
parse_single_object_field(state, &mut fields)?;
handle_object_field_separator(state)?;
}
state.tokens.expect(&Token::RightBrace)?;
Ok(Expr::new(ExprKind::ObjectLiteral { fields }, start_span))
}
fn parse_single_object_field(state: &mut ParserState, fields: &mut Vec<ObjectField>) -> Result<()> {
if matches!(state.tokens.peek(), Some((Token::DotDotDot, _))) {
parse_object_spread_field(state, fields)
} else {
parse_object_key_value_field(state, fields)
}
}
fn parse_object_spread_field(state: &mut ParserState, fields: &mut Vec<ObjectField>) -> Result<()> {
state.tokens.advance(); let expr = super::parse_expr_recursive(state)?;
fields.push(ObjectField::Spread { expr });
Ok(())
}
fn parse_object_key_value_field(
state: &mut ParserState,
fields: &mut Vec<ObjectField>,
) -> Result<()> {
let key = parse_object_key(state)?;
if matches!(state.tokens.peek(), Some((Token::FatArrow, _))) {
state.tokens.advance(); } else {
state.tokens.expect(&Token::Colon)?;
}
let value = super::parse_expr_recursive(state)?;
fields.push(ObjectField::KeyValue { key, value });
Ok(())
}
fn handle_object_field_separator(state: &mut ParserState) -> Result<()> {
if matches!(state.tokens.peek(), Some((Token::Comma, _))) {
state.tokens.advance();
Ok(())
} else if !matches!(state.tokens.peek(), Some((Token::RightBrace, _))) {
bail!("Expected comma or closing brace in object literal")
} else {
Ok(())
}
}
pub fn parse_list(state: &mut ParserState) -> Result<Expr> {
let start_span = state.tokens.advance().expect("checked by parser logic").1; if matches!(state.tokens.peek(), Some((Token::RightBracket, _))) {
state.tokens.advance(); return Ok(Expr::new(ExprKind::List(Vec::new()), start_span));
}
let first_element = parse_list_element(state)?;
if matches!(state.tokens.peek(), Some((Token::For, _))) {
return parse_list_comprehension(state, start_span, first_element);
}
let mut elements = vec![first_element];
while matches!(state.tokens.peek(), Some((Token::Comma, _))) {
state.tokens.advance(); if matches!(state.tokens.peek(), Some((Token::RightBracket, _))) {
break; }
elements.push(parse_list_element(state)?);
}
state.tokens.expect(&Token::RightBracket)?;
Ok(Expr::new(ExprKind::List(elements), start_span))
}
fn parse_list_element(state: &mut ParserState) -> Result<Expr> {
if matches!(state.tokens.peek(), Some((Token::DotDotDot, _))) {
let start_pos = state.tokens.advance().expect("checked above").1.start; let expr = super::parse_expr_recursive(state)?;
let span = Span {
start: start_pos,
end: expr.span.end,
};
Ok(Expr::new(
ExprKind::Spread {
expr: Box::new(expr),
},
span,
))
} else {
super::parse_expr_recursive(state)
}
}
fn parse_comprehension_expr(state: &mut ParserState) -> Result<Expr> {
let mut left = parse_condition_term(state)?;
while let Some((token, _)) = state.tokens.peek() {
match token {
Token::For | Token::If | Token::RightBracket => break,
Token::DotDot | Token::DotDotEqual => {
let is_inclusive = matches!(token, Token::DotDotEqual);
state.tokens.advance(); let end = parse_condition_term(state)?;
left = Expr::new(
ExprKind::Range {
start: Box::new(left),
end: Box::new(end),
inclusive: is_inclusive,
},
Span::default(),
);
}
Token::Greater
| Token::Less
| Token::GreaterEqual
| Token::LessEqual
| Token::EqualEqual
| Token::NotEqual
| Token::AndAnd
| Token::OrOr
| Token::Plus
| Token::Minus
| Token::Star
| Token::Slash
| Token::Percent => {
let op = expressions::token_to_binary_op(token).expect("checked: valid op");
state.tokens.advance(); let right = parse_condition_term(state)?;
left = Expr::new(
ExprKind::Binary {
left: Box::new(left),
op,
right: Box::new(right),
},
Span::default(),
);
}
_ => break, }
}
Ok(left)
}
fn parse_condition_expr(state: &mut ParserState) -> Result<Expr> {
let _start_pos = state.tokens.position();
let mut left = parse_condition_term(state)?;
while let Some((token, _)) = state.tokens.peek() {
match token {
Token::Greater
| Token::Less
| Token::GreaterEqual
| Token::LessEqual
| Token::EqualEqual
| Token::NotEqual
| Token::AndAnd
| Token::OrOr
| Token::Plus
| Token::Minus
| Token::Star
| Token::Slash
| Token::Percent => {
let op = expressions::token_to_binary_op(token).expect("checked: valid op");
state.tokens.advance(); let right = parse_condition_term(state)?;
left = Expr::new(
ExprKind::Binary {
left: Box::new(left),
op,
right: Box::new(right),
},
Span { start: 0, end: 0 },
);
}
_ => break, }
}
Ok(left)
}
fn parse_condition_term(state: &mut ParserState) -> Result<Expr> {
let mut expr = expressions::parse_prefix(state)?;
while let Some((token, _)) = state.tokens.peek() {
expr = match token {
Token::Dot => parse_dot_operation(state, expr)?,
Token::LeftParen => functions::parse_call(state, expr)?,
_ => break, };
}
Ok(expr)
}
fn parse_dot_operation(state: &mut ParserState, expr: Expr) -> Result<Expr> {
state.tokens.advance(); let Some((Token::Identifier(name), _)) = state.tokens.peek() else {
return Ok(expr); };
let name = name.clone();
state.tokens.advance();
if matches!(state.tokens.peek(), Some((Token::LeftParen, _))) {
parse_method_call(state, expr, name)
} else {
Ok(create_field_access(expr, name))
}
}
fn parse_method_call(state: &mut ParserState, receiver: Expr, method: String) -> Result<Expr> {
state.tokens.advance(); let args = parse_method_arguments(state)?;
state.tokens.expect(&Token::RightParen)?;
Ok(Expr::new(
ExprKind::MethodCall {
receiver: Box::new(receiver),
method,
args,
},
Span { start: 0, end: 0 },
))
}
fn parse_method_arguments(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 matches!(state.tokens.peek(), Some((Token::Comma, _))) {
state.tokens.advance();
} else {
break;
}
}
Ok(args)
}
fn create_field_access(object: Expr, field: String) -> Expr {
Expr::new(
ExprKind::FieldAccess {
object: Box::new(object),
field,
},
Span { start: 0, end: 0 },
)
}
pub fn parse_list_comprehension(
state: &mut ParserState,
start_span: Span,
element: Expr,
) -> Result<Expr> {
let mut clauses = Vec::new();
state.tokens.expect(&Token::For)?;
clauses.push(parse_for_clause(state)?);
while matches!(state.tokens.peek(), Some((Token::For, _))) {
state.tokens.advance();
clauses.push(parse_for_clause(state)?);
}
state.tokens.expect(&Token::RightBracket)?;
Ok(Expr::new(
ExprKind::ListComprehension {
element: Box::new(element),
clauses,
},
start_span,
))
}
fn parse_for_clause(state: &mut ParserState) -> Result<crate::frontend::ast::ComprehensionClause> {
let variable = parse_comprehension_variable(state)?;
state.tokens.expect(&Token::In)?;
let iterable = parse_comprehension_iterable(state)?;
let condition = if matches!(state.tokens.peek(), Some((Token::If, _))) {
state.tokens.advance();
Some(Box::new(parse_condition_expr(state)?))
} else {
None
};
Ok(crate::frontend::ast::ComprehensionClause {
variable,
iterable: Box::new(iterable),
condition,
})
}
fn parse_comprehension_iterable(state: &mut ParserState) -> Result<Expr> {
let mut expr = parse_comprehension_expr(state)?;
while let Some((token, _)) = state.tokens.peek() {
match token {
Token::For | Token::If | Token::RightBracket => break,
Token::Dot => {
state.tokens.advance(); if let Some((Token::Identifier(method), _)) = state.tokens.peek() {
let method_name = method.clone();
state.tokens.advance();
expr = Expr::new(
ExprKind::FieldAccess {
object: Box::new(expr),
field: method_name,
},
Span::default(),
);
}
}
_ => break,
}
}
Ok(expr)
}
fn parse_dataframe_header(state: &mut ParserState) -> Result<Span> {
let start_span = state.tokens.advance().expect("checked by parser logic").1; state.tokens.expect(&Token::Bang)?;
state.tokens.expect(&Token::LeftBracket)?;
Ok(start_span)
}
fn parse_dataframe_column_name(state: &mut ParserState) -> Result<String> {
match state.tokens.peek() {
Some((Token::Identifier(name), _)) => {
let name = name.clone();
state.tokens.advance();
Ok(name)
}
Some((Token::String(name), _)) => {
let name = name.clone();
state.tokens.advance();
Ok(name)
}
_ => bail!("Expected column name (identifier or string) in DataFrame literal"),
}
}
fn parse_dataframe_column_values(state: &mut ParserState) -> Result<Vec<Expr>> {
state.tokens.expect(&Token::FatArrow)?; let values = if matches!(state.tokens.peek(), Some((Token::LeftBracket, _))) {
parse_list(state)?
} else {
super::parse_expr_recursive(state)?
};
let value_vec = match values.kind {
ExprKind::List(exprs) => exprs,
_ => vec![values],
};
Ok(value_vec)
}
fn handle_dataframe_legacy_syntax_column(col_name: String) -> DataFrameColumn {
DataFrameColumn {
name: col_name,
values: Vec::new(),
}
}
pub fn parse_dataframe_column_definitions(state: &mut ParserState) -> Result<Vec<DataFrameColumn>> {
let mut columns = Vec::new();
loop {
if matches!(state.tokens.peek(), Some((Token::RightBracket, _))) {
break;
}
let col_name = parse_dataframe_column_name(state)?;
parse_single_dataframe_column(state, col_name, &mut columns)?;
if !handle_dataframe_column_continuation(state, &mut columns)? {
break;
}
}
Ok(columns)
}
fn parse_single_dataframe_column(
state: &mut ParserState,
col_name: String,
columns: &mut Vec<DataFrameColumn>,
) -> Result<()> {
if matches!(state.tokens.peek(), Some((Token::FatArrow, _))) {
let values = parse_dataframe_column_values(state)?;
columns.push(DataFrameColumn {
name: col_name,
values,
});
} else if is_dataframe_legacy_syntax_token(state) {
columns.push(handle_dataframe_legacy_syntax_column(col_name));
} else {
bail!("Expected '=>' or ',' after column name in DataFrame literal");
}
Ok(())
}
fn is_dataframe_legacy_syntax_token(state: &mut ParserState) -> bool {
matches!(state.tokens.peek(), Some((Token::Comma, _)))
|| matches!(state.tokens.peek(), Some((Token::Semicolon, _)))
|| matches!(state.tokens.peek(), Some((Token::RightBracket, _)))
}
fn handle_dataframe_column_continuation(
state: &mut ParserState,
columns: &mut Vec<DataFrameColumn>,
) -> Result<bool> {
if matches!(state.tokens.peek(), Some((Token::Comma, _))) {
state.tokens.advance();
Ok(true)
} else if matches!(state.tokens.peek(), Some((Token::Semicolon, _))) {
state.tokens.advance();
parse_legacy_dataframe_rows(state, columns)?;
Ok(false)
} else {
Ok(false)
}
}
fn create_dataframe_result(columns: Vec<DataFrameColumn>, start_span: Span) -> Result<Expr> {
Ok(Expr::new(ExprKind::DataFrame { columns }, start_span))
}
pub fn parse_dataframe(state: &mut ParserState) -> Result<Expr> {
let start_span = parse_dataframe_header(state)?;
if matches!(state.tokens.peek(), Some((Token::RightBracket, _))) {
state.tokens.advance();
return create_dataframe_result(Vec::new(), start_span);
}
let columns = parse_dataframe_column_definitions(state)?;
state.tokens.expect(&Token::RightBracket)?;
create_dataframe_result(columns, start_span)
}
#[allow(clippy::ptr_arg)] fn parse_legacy_dataframe_rows(
state: &mut ParserState,
columns: &mut Vec<DataFrameColumn>,
) -> Result<()> {
let rows = parse_all_dataframe_rows(state)?;
populate_dataframe_columns(columns, &rows);
Ok(())
}
fn parse_all_dataframe_rows(state: &mut ParserState) -> Result<Vec<Vec<Expr>>> {
let mut rows = Vec::new();
loop {
if is_end_bracket(state) {
break;
}
let row = parse_single_dataframe_row(state)?;
add_non_empty_row(&mut rows, row);
if !consume_row_separator(state) {
break;
}
}
Ok(rows)
}
fn is_end_bracket(state: &mut ParserState) -> bool {
matches!(state.tokens.peek(), Some((Token::RightBracket, _)))
}
fn parse_single_dataframe_row(state: &mut ParserState) -> Result<Vec<Expr>> {
let mut row = Vec::new();
loop {
if is_row_boundary(state) {
break;
}
row.push(super::parse_expr_recursive(state)?);
if !consume_value_separator(state) {
break;
}
}
Ok(row)
}
fn is_row_boundary(state: &mut ParserState) -> bool {
matches!(state.tokens.peek(), Some((Token::Semicolon, _)))
|| matches!(state.tokens.peek(), Some((Token::RightBracket, _)))
}
fn consume_value_separator(state: &mut ParserState) -> bool {
if matches!(state.tokens.peek(), Some((Token::Comma, _))) {
state.tokens.advance();
true
} else {
false
}
}
fn consume_row_separator(state: &mut ParserState) -> bool {
if matches!(state.tokens.peek(), Some((Token::Semicolon, _))) {
state.tokens.advance();
true
} else {
false
}
}
fn add_non_empty_row(rows: &mut Vec<Vec<Expr>>, row: Vec<Expr>) {
if !row.is_empty() {
rows.push(row);
}
}
fn populate_dataframe_columns(columns: &mut [DataFrameColumn], rows: &[Vec<Expr>]) {
for (col_idx, column) in columns.iter_mut().enumerate() {
for row in rows {
if col_idx < row.len() {
column.values.push(row[col_idx].clone());
}
}
}
}
fn parse_comprehension_element(state: &mut ParserState) -> Result<Expr> {
let mut expr = super::expressions::parse_prefix(state)?;
loop {
if matches!(state.tokens.peek(), Some((Token::For, _))) {
break;
}
let prev_expr = expr.clone();
expr = super::handle_postfix_operators(state, expr)?;
if expr != prev_expr {
continue; }
if let Some(new_expr) = super::try_handle_infix_operators(state, expr.clone(), 0)? {
expr = new_expr;
continue;
}
break;
}
Ok(expr)
}
fn try_parse_comprehension(state: &mut ParserState, start_span: Span) -> Result<Expr> {
if !looks_like_comprehension(state) {
bail!("Not a comprehension - doesn't match comprehension pattern");
}
let saved_position = state.tokens.position();
let first_expr = match parse_comprehension_element(state) {
Ok(expr) => expr,
Err(e) => {
state.tokens.set_position(saved_position);
bail!("Not a comprehension - failed to parse first expression: {e}");
}
};
match state.tokens.peek() {
Some((Token::For, _)) => {
parse_set_comprehension_continuation(state, first_expr, start_span)
}
Some((Token::Colon, _)) => {
state.tokens.advance(); let value_expr = match parse_comprehension_element(state) {
Ok(expr) => expr,
Err(e) => {
state.tokens.set_position(saved_position);
bail!("Not a dict comprehension - failed to parse value: {e}");
}
};
if matches!(state.tokens.peek(), Some((Token::For, _))) {
parse_dict_comprehension_continuation(state, first_expr, value_expr, start_span)
} else {
state.tokens.set_position(saved_position);
bail!("Not a dict comprehension - no 'for' keyword");
}
}
_ => {
state.tokens.set_position(saved_position);
bail!("Not a comprehension - no 'for' or ':' after first expression");
}
}
}
fn looks_like_comprehension(state: &mut ParserState) -> bool {
let saved_pos = state.tokens.position();
let mut token_count = 0;
let mut found_for = false;
let mut nesting_depth = 0;
while token_count < 20 && !found_for {
match state.tokens.peek() {
Some((Token::For, _)) if nesting_depth == 0 => {
if token_count == 0 {
break; }
found_for = true;
break;
}
Some((Token::LeftBrace | Token::LeftParen | Token::LeftBracket, _)) => {
nesting_depth += 1;
state.tokens.advance();
token_count += 1;
}
Some((Token::RightBrace | Token::RightParen | Token::RightBracket, _)) => {
if nesting_depth > 0 {
nesting_depth -= 1;
state.tokens.advance();
token_count += 1;
} else {
break;
}
}
Some((Token::Semicolon | Token::Let | Token::Var, _)) if nesting_depth == 0 => {
break;
}
Some(_) => {
state.tokens.advance();
token_count += 1;
}
None => break,
}
}
state.tokens.set_position(saved_pos);
found_for
}
fn parse_set_comprehension_continuation(
state: &mut ParserState,
element: Expr,
start_span: Span,
) -> Result<Expr> {
let mut clauses = Vec::new();
state.tokens.expect(&Token::For)?;
clauses.push(parse_for_clause(state)?);
while matches!(state.tokens.peek(), Some((Token::For, _))) {
state.tokens.advance();
clauses.push(parse_for_clause(state)?);
}
state.tokens.expect(&Token::RightBrace)?;
Ok(Expr::new(
crate::frontend::ast::ExprKind::SetComprehension {
element: Box::new(element),
clauses,
},
start_span,
))
}
fn parse_dict_comprehension_continuation(
state: &mut ParserState,
key: Expr,
value: Expr,
start_span: Span,
) -> Result<Expr> {
let mut clauses = Vec::new();
state.tokens.expect(&Token::For)?;
clauses.push(parse_for_clause(state)?);
while matches!(state.tokens.peek(), Some((Token::For, _))) {
state.tokens.advance();
clauses.push(parse_for_clause(state)?);
}
state.tokens.expect(&Token::RightBrace)?;
Ok(Expr::new(
crate::frontend::ast::ExprKind::DictComprehension {
key: Box::new(key),
value: Box::new(value),
clauses,
},
start_span,
))
}
pub fn parse_comprehension_variable(state: &mut ParserState) -> Result<String> {
match state.tokens.peek() {
Some((Token::LeftParen, _)) => parse_tuple_pattern(state),
Some((Token::Identifier(_), _)) => parse_identifier_pattern(state),
Some((Token::Some, _)) => parse_option_some_pattern(state),
Some((Token::None, _)) => parse_option_none_pattern(state),
Some((Token::Ok, _)) => parse_result_ok_pattern(state),
Some((Token::Err, _)) => parse_result_err_pattern(state),
_ => bail!("Expected pattern in comprehension variable"),
}
}
fn parse_tuple_pattern(state: &mut ParserState) -> Result<String> {
state.tokens.advance(); let mut pattern_str = String::from("(");
if let Some((Token::Identifier(name), _)) = state.tokens.peek() {
pattern_str.push_str(name);
state.tokens.advance();
} else {
bail!("Expected identifier in tuple pattern");
}
while matches!(state.tokens.peek(), Some((Token::Comma, _))) {
state.tokens.advance();
pattern_str.push_str(", ");
if let Some((Token::Identifier(name), _)) = state.tokens.peek() {
pattern_str.push_str(name);
state.tokens.advance();
} else {
bail!("Expected identifier after comma in tuple pattern");
}
}
state.tokens.expect(&Token::RightParen)?;
pattern_str.push(')');
Ok(pattern_str)
}
fn parse_identifier_pattern(state: &mut ParserState) -> Result<String> {
let name = if let Some((Token::Identifier(n), _)) = state.tokens.peek() {
n.clone()
} else {
bail!("Expected identifier")
};
state.tokens.advance();
if matches!(state.tokens.peek(), Some((Token::LeftParen, _))) {
parse_constructor_pattern(state, &name)
} else {
Ok(name)
}
}
fn parse_constructor_pattern(state: &mut ParserState, name: &str) -> Result<String> {
state.tokens.advance(); let mut pattern_str = format!("{name}(");
if let Some((Token::Identifier(inner), _)) = state.tokens.peek() {
pattern_str.push_str(inner);
state.tokens.advance();
}
state.tokens.expect(&Token::RightParen)?;
pattern_str.push(')');
Ok(pattern_str)
}
fn parse_option_some_pattern(state: &mut ParserState) -> Result<String> {
state.tokens.advance(); state.tokens.expect(&Token::LeftParen)?;
let inner = if let Some((Token::Identifier(name), _)) = state.tokens.peek() {
let name = name.clone();
state.tokens.advance();
name
} else {
bail!("Expected identifier inside Some pattern");
};
state.tokens.expect(&Token::RightParen)?;
Ok(format!("Some({inner})"))
}
fn parse_option_none_pattern(state: &mut ParserState) -> Result<String> {
state.tokens.advance();
Ok("None".to_string())
}
fn parse_result_ok_pattern(state: &mut ParserState) -> Result<String> {
state.tokens.advance(); state.tokens.expect(&Token::LeftParen)?;
let inner = if let Some((Token::Identifier(name), _)) = state.tokens.peek() {
let name = name.clone();
state.tokens.advance();
name
} else {
bail!("Expected identifier inside Ok pattern");
};
state.tokens.expect(&Token::RightParen)?;
Ok(format!("Ok({inner})"))
}
fn parse_result_err_pattern(state: &mut ParserState) -> Result<String> {
state.tokens.advance(); state.tokens.expect(&Token::LeftParen)?;
let inner = if let Some((Token::Identifier(name), _)) = state.tokens.peek() {
let name = name.clone();
state.tokens.advance();
name
} else {
bail!("Expected identifier inside Err pattern");
};
state.tokens.expect(&Token::RightParen)?;
Ok(format!("Err({inner})"))
}
fn try_parse_set_literal(state: &mut ParserState, start_span: Span) -> Result<Expr> {
let saved_position = state.tokens.position();
if matches!(state.tokens.peek(), Some((Token::RightBrace, _))) {
state.tokens.advance();
return Ok(Expr::new(ExprKind::Set(Vec::new()), start_span));
}
let mut elements = Vec::new();
loop {
if matches!(state.tokens.peek(), Some((Token::RightBrace, _))) {
break;
}
if let Some((Token::Let | Token::If | Token::For | Token::While | Token::Return, _)) =
state.tokens.peek()
{
state.tokens.set_position(saved_position);
bail!("Not a set literal - contains statements");
}
let expr = if let Ok(expr) = super::parse_expr_recursive(state) {
expr
} else {
state.tokens.set_position(saved_position);
bail!("Not a set literal - failed to parse expression");
};
match state.tokens.peek() {
Some((Token::Comma, _)) => {
elements.push(expr);
state.tokens.advance();
if matches!(state.tokens.peek(), Some((Token::RightBrace, _))) {
break;
}
}
Some((Token::RightBrace, _)) => {
elements.push(expr);
break;
}
Some((Token::Semicolon, _)) => {
state.tokens.set_position(saved_position);
bail!("Not a set literal - contains semicolon");
}
Some((Token::For, _)) => {
state.tokens.set_position(saved_position);
bail!("Not a set literal - looks like comprehension");
}
_ => {
state.tokens.set_position(saved_position);
bail!("Not a set literal - unexpected token after expression");
}
}
}
if elements.is_empty() {
state.tokens.set_position(saved_position);
bail!("Not a set literal - no elements");
}
state.tokens.expect(&Token::RightBrace)?;
Ok(Expr::new(ExprKind::Set(elements), start_span))
}
#[cfg(test)]
mod tests {
use crate::frontend::parser::Parser;
#[test]
fn test_parse_empty_list() {
let mut parser = Parser::new("[]");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse empty list");
}
#[test]
fn test_parse_simple_list() {
let mut parser = Parser::new("[1, 2, 3]");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse simple list");
}
#[test]
fn test_parse_nested_list() {
let mut parser = Parser::new("[[1, 2], [3, 4]]");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse nested list");
}
#[test]
fn test_parse_list_with_mixed_types() {
let mut parser = Parser::new("[1, \"hello\", true, 3.15]");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse list with mixed types");
}
#[test]
fn test_parse_empty_block() {
let mut parser = Parser::new("{}");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse empty block");
}
#[test]
fn test_parse_block_with_statements() {
let mut parser = Parser::new("{ let x = 5; x + 1 }");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse block with statements");
}
#[test]
fn test_parse_nested_blocks() {
let mut parser = Parser::new("{ { 42 } }");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse nested blocks");
}
#[test]
fn test_parse_object_literal_empty() {
let mut parser = Parser::new("{}");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse empty object literal");
}
#[test]
fn test_parse_object_literal_with_fields() {
let mut parser = Parser::new("{name: \"Alice\", age: 30}");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse object literal with fields");
}
#[test]
fn test_parse_object_literal_quoted_keys() {
let mut parser = Parser::new("{\"key\": \"value\"}");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse object with quoted keys");
}
#[test]
fn test_parse_list_comprehension_simple() {
let mut parser = Parser::new("[x * 2 for x in range(10)]");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse simple list comprehension");
}
#[test]
fn test_parse_list_comprehension_with_filter() {
let mut parser = Parser::new("[x for x in range(10) if x % 2 == 0]");
let result = parser.parse();
assert!(
result.is_ok(),
"Failed to parse list comprehension with filter"
);
}
#[test]
#[ignore = "DataFrame macro not yet implemented"]
fn test_parse_dataframe_empty() {
let mut parser = Parser::new("df![]");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse empty dataframe");
}
#[test]
#[ignore = "DataFrame macro not yet implemented"]
fn test_parse_dataframe_with_columns() {
let mut parser = Parser::new("df![[1, 4], [2, 5], [3, 6]]");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse dataframe with columns");
}
#[test]
#[ignore = "DataFrame macro not yet implemented"]
fn test_parse_dataframe_with_rows() {
let mut parser = Parser::new("df![[1, 2, 3], [4, 5, 6]]");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse dataframe with rows");
}
#[test]
#[ignore = "DataFrame macro not yet implemented"]
fn test_parse_dataframe_macro() {
let mut parser = Parser::new("df![[1, 2, 3], [4, 5, 6]]");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse dataframe macro");
}
#[test]
fn test_parse_block_with_multiple_expressions() {
let mut parser = Parser::new("{ 1; 2; 3 }");
let result = parser.parse();
assert!(
result.is_ok(),
"Failed to parse block with multiple expressions"
);
}
#[test]
fn test_parse_block_with_let_binding() {
let mut parser = Parser::new("{ let x = 10; x }");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse block with let binding");
}
#[test]
fn test_parse_let_expression() {
let mut parser = Parser::new("let x = 5 in x + 1");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse let expression");
}
#[test]
fn test_parse_object_with_nested_objects() {
let mut parser = Parser::new("{outer: {inner: 42}}");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse nested objects");
}
#[test]
fn test_parse_list_with_trailing_comma() {
let mut parser = Parser::new("[1, 2, 3,]");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse list with trailing comma");
}
#[test]
fn test_parse_object_with_trailing_comma() {
let mut parser = Parser::new("{a: 1, b: 2,}");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse object with trailing comma");
}
#[test]
fn test_parse_complex_nested_structure() {
let mut parser = Parser::new("[{a: [1, 2]}, {b: [3, 4]}]");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse complex nested structure");
}
#[test]
fn test_parse_block_returns_last_expression() {
let mut parser = Parser::new("{ 1; 2; 3 }");
let result = parser.parse();
assert!(
result.is_ok(),
"Failed to parse block that returns last expression"
);
}
#[test]
fn test_parse_list_with_expressions() {
let mut parser = Parser::new("[1 + 2, 3 * 4, 5 - 6]");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse list with expressions");
}
#[test]
fn test_parse_object_with_computed_values() {
let mut parser = Parser::new("{sum: 1 + 2, product: 3 * 4}");
let result = parser.parse();
assert!(
result.is_ok(),
"Failed to parse object with computed values"
);
}
#[test]
#[ignore = "DataFrame macro not yet implemented"]
fn test_parse_dataframe_semicolon_rows() {
let mut parser = Parser::new("df![[1, 2], [3, 4], [5, 6]]");
let result = parser.parse();
assert!(
result.is_ok(),
"Failed to parse dataframe with semicolon-separated rows"
);
}
#[test]
fn test_parse_empty_list_comprehension() {
let mut parser = Parser::new("[x for x in []]");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse empty list comprehension");
}
#[test]
fn test_parse_nested_list_comprehension() {
let mut parser = Parser::new("[[x * y for x in range(3)] for y in range(3)]");
let result = parser.parse();
assert!(result.is_ok(), "Failed to parse nested list comprehension");
}
#[test]
fn test_parse_object_shorthand_properties() {
let mut parser = Parser::new("{x: x, y: y, z: z}");
let result = parser.parse();
assert!(
result.is_ok(),
"Failed to parse object with shorthand properties"
);
}
#[test]
fn test_looks_like_comprehension_with_for() {
let mut parser = Parser::new("[x for x in range(10)]");
let result = parser.parse();
assert!(result.is_ok(), "List comprehension with 'for' should parse");
}
#[test]
fn test_parse_constructor_pattern_returns_actual_string() {
let mut parser = Parser::new("match x { Point(a, b) => a + b }");
let result = parser.parse();
assert!(
result.is_ok(),
"Constructor pattern should parse with actual data"
);
}
#[test]
fn test_declaration_token_var_match_arm() {
let mut parser = Parser::new("var x = 42");
let result = parser.parse();
assert!(result.is_ok(), "Should parse 'var' declaration token");
}
#[test]
fn test_declaration_token_pub_match_arm() {
let mut parser = Parser::new("pub fn foo() {}");
let result = parser.parse();
assert!(result.is_ok(), "Should parse 'pub' declaration token");
}
#[test]
fn test_add_non_empty_row_negation() {
let mut parser = Parser::new("[1, 2, 3]");
let result = parser.parse();
assert!(
result.is_ok(),
"Non-empty row array should parse (validates ! operator logic)"
);
}
#[test]
fn test_try_parse_set_literal_right_brace_match_arm() {
let mut parser = Parser::new("{1, 2, 3}");
let result = parser.parse();
assert!(result.is_ok(), "Expression with RightBrace should parse");
}
#[test]
fn test_try_parse_set_literal_semicolon_detection() {
let mut parser = Parser::new("{let x = 1; x}");
let result = parser.parse();
assert!(result.is_ok(), "Block with semicolon should parse");
}
#[test]
fn test_is_dataframe_legacy_syntax_token_returns_bool() {
let mut parser = Parser::new("{column: [1, 2, 3]}");
let result = parser.parse();
assert!(
result.is_ok(),
"Object with array values should parse (validates boolean logic)"
);
}
#[test]
fn test_parse_all_dataframe_rows_returns_actual_data() {
let mut parser = Parser::new("[[1, 2], [3, 4]]");
let result = parser.parse();
assert!(
result.is_ok(),
"Nested arrays should parse (validates row data logic)"
);
}
#[test]
fn test_parser_082_atom_map_key_simple() {
let mut parser = Parser::new("{ :host => \"localhost\" }");
let result = parser.parse();
assert!(result.is_ok(), "Atom as map key should parse");
}
#[test]
fn test_parser_082_atom_map_key_multiple() {
let mut parser = Parser::new("{ :host => \"localhost\", :port => 8080 }");
let result = parser.parse();
assert!(result.is_ok(), "Multiple atom keys should parse");
}
#[test]
fn test_parser_082_atom_map_key_with_colon() {
let mut parser = Parser::new("{ :status: :ok }");
let result = parser.parse();
assert!(result.is_ok(), "Atom key with colon separator should parse");
}
#[test]
fn test_parser_082_atom_map_key_mixed() {
let mut parser = Parser::new("{ :name => \"test\", count: 42 }");
let result = parser.parse();
assert!(
result.is_ok(),
"Mixed atom and identifier keys should parse"
);
}
}
#[cfg(test)]
mod mutation_tests {
use super::*;
#[test]
fn test_looks_like_comprehension_negation() {
use crate::Parser;
let mut parser = Parser::new("[x for x in range(10)]");
let result = parser.parse();
assert!(
result.is_ok(),
"Array comprehension should parse (tests ! in while condition)"
);
let mut parser2 = Parser::new("[1, 2, 3, 4, 5]");
let result2 = parser2.parse();
assert!(result2.is_ok(), "Regular array should parse");
}
#[test]
fn test_parse_constructor_pattern_not_stub() {
use crate::Parser;
let mut parser = Parser::new("match value { Some(x) => x, None => 0 }");
let result = parser.parse();
assert!(
result.is_ok(),
"Enum constructor pattern should parse correctly"
);
}
#[test]
fn test_declaration_token_to_key_var_match_arm() {
let result = declaration_token_to_key(&Token::Var);
assert!(result.is_some(), "Token::Var should map to a key");
assert_eq!(
result.unwrap(),
"var",
"Token::Var should map to 'var' string"
);
}
#[test]
fn test_add_non_empty_row_negation() {
use crate::Parser;
let mut parser = Parser::new("[[1, 2], [3, 4]]");
let result = parser.parse();
assert!(
result.is_ok(),
"Nested arrays should parse (tests ! in add_non_empty_row)"
);
}
#[test]
fn test_control_flow_token_to_key() {
assert_eq!(
control_flow_token_to_key(&Token::If),
Some("if".to_string())
);
assert_eq!(
control_flow_token_to_key(&Token::Else),
Some("else".to_string())
);
assert_eq!(
control_flow_token_to_key(&Token::Match),
Some("match".to_string())
);
assert_eq!(
control_flow_token_to_key(&Token::While),
Some("while".to_string())
);
assert_eq!(
control_flow_token_to_key(&Token::For),
Some("for".to_string())
);
assert_eq!(
control_flow_token_to_key(&Token::Loop),
Some("loop".to_string())
);
assert_eq!(
control_flow_token_to_key(&Token::Break),
Some("break".to_string())
);
assert_eq!(
control_flow_token_to_key(&Token::Continue),
Some("continue".to_string())
);
assert_eq!(
control_flow_token_to_key(&Token::Return),
Some("return".to_string())
);
assert_eq!(control_flow_token_to_key(&Token::Plus), None);
}
#[test]
fn test_declaration_token_to_key_all() {
assert_eq!(
declaration_token_to_key(&Token::Let),
Some("let".to_string())
);
assert_eq!(
declaration_token_to_key(&Token::Var),
Some("var".to_string())
);
assert_eq!(
declaration_token_to_key(&Token::Const),
Some("const".to_string())
);
assert_eq!(
declaration_token_to_key(&Token::Static),
Some("static".to_string())
);
assert_eq!(
declaration_token_to_key(&Token::Pub),
Some("pub".to_string())
);
assert_eq!(
declaration_token_to_key(&Token::Mut),
Some("mut".to_string())
);
assert_eq!(
declaration_token_to_key(&Token::Fun),
Some("fun".to_string())
);
assert_eq!(declaration_token_to_key(&Token::Fn), Some("fn".to_string()));
assert_eq!(declaration_token_to_key(&Token::Plus), None);
}
#[test]
fn test_type_token_to_key() {
assert_eq!(type_token_to_key(&Token::Type), Some("type".to_string()));
assert_eq!(
type_token_to_key(&Token::Struct),
Some("struct".to_string())
);
assert_eq!(type_token_to_key(&Token::Enum), Some("enum".to_string()));
assert_eq!(type_token_to_key(&Token::Impl), Some("impl".to_string()));
assert_eq!(type_token_to_key(&Token::Trait), Some("trait".to_string()));
assert_eq!(type_token_to_key(&Token::Plus), None);
}
#[test]
fn test_module_token_to_key() {
assert_eq!(
module_token_to_key(&Token::Module),
Some("module".to_string())
);
assert_eq!(
module_token_to_key(&Token::Import),
Some("import".to_string())
);
assert_eq!(
module_token_to_key(&Token::Export),
Some("export".to_string())
);
assert_eq!(module_token_to_key(&Token::Use), Some("use".to_string()));
assert_eq!(module_token_to_key(&Token::As), Some("as".to_string()));
assert_eq!(module_token_to_key(&Token::From), Some("from".to_string()));
assert_eq!(module_token_to_key(&Token::Self_), Some("self".to_string()));
assert_eq!(
module_token_to_key(&Token::Super),
Some("super".to_string())
);
assert_eq!(
module_token_to_key(&Token::Crate),
Some("crate".to_string())
);
assert_eq!(module_token_to_key(&Token::In), Some("in".to_string()));
assert_eq!(
module_token_to_key(&Token::Where),
Some("where".to_string())
);
assert_eq!(module_token_to_key(&Token::Plus), None);
}
#[test]
fn test_async_error_token_to_key() {
assert_eq!(
async_error_token_to_key(&Token::Async),
Some("async".to_string())
);
assert_eq!(
async_error_token_to_key(&Token::Await),
Some("await".to_string())
);
assert_eq!(
async_error_token_to_key(&Token::Try),
Some("try".to_string())
);
assert_eq!(
async_error_token_to_key(&Token::Catch),
Some("catch".to_string())
);
assert_eq!(
async_error_token_to_key(&Token::Throw),
Some("throw".to_string())
);
assert_eq!(async_error_token_to_key(&Token::Plus), None);
}
#[test]
fn test_can_be_object_key() {
use crate::frontend::lexer::Token;
assert!(can_be_object_key(&Token::Identifier("name".to_string())));
assert!(can_be_object_key(&Token::String("key".to_string())));
assert!(can_be_object_key(&Token::If));
assert!(can_be_object_key(&Token::While));
assert!(!can_be_object_key(&Token::Plus));
assert!(!can_be_object_key(&Token::LeftParen));
}
#[test]
fn test_parse_block_empty() {
use crate::Parser;
let mut parser = Parser::new("{}");
let result = parser.parse();
assert!(result.is_ok());
}
#[test]
fn test_parse_block_single_expr() {
use crate::Parser;
let mut parser = Parser::new("{ 42 }");
let result = parser.parse();
assert!(result.is_ok());
}
#[test]
fn test_parse_block_multiple_exprs() {
use crate::Parser;
let mut parser = Parser::new("{ let x = 1; let y = 2; x + y }");
let result = parser.parse();
assert!(result.is_ok());
}
#[test]
fn test_parse_array_empty() {
use crate::Parser;
let mut parser = Parser::new("[]");
let result = parser.parse();
assert!(result.is_ok());
}
#[test]
fn test_parse_array_single() {
use crate::Parser;
let mut parser = Parser::new("[1]");
let result = parser.parse();
assert!(result.is_ok());
}
#[test]
fn test_parse_array_multiple() {
use crate::Parser;
let mut parser = Parser::new("[1, 2, 3, 4, 5]");
let result = parser.parse();
assert!(result.is_ok());
}
#[test]
fn test_parse_array_nested() {
use crate::Parser;
let mut parser = Parser::new("[[1, 2], [3, 4], [5, 6]]");
let result = parser.parse();
assert!(result.is_ok());
}
#[test]
fn test_parse_map_constructor() {
use crate::Parser;
let mut parser = Parser::new("HashMap()");
let result = parser.parse();
assert!(result.is_ok());
}
#[test]
fn test_parse_object_with_string_key() {
use crate::Parser;
let mut parser = Parser::new("{ \"key\": 42 }");
let result = parser.parse();
assert!(result.is_ok());
}
#[test]
fn test_parse_set_braces() {
use crate::Parser;
let mut parser = Parser::new("{1, 2, 3}");
let result = parser.parse();
assert!(result.is_ok());
}
#[test]
fn test_parse_tuple_single() {
use crate::Parser;
let mut parser = Parser::new("(1,)");
let result = parser.parse();
assert!(result.is_ok());
}
#[test]
fn test_parse_tuple_multiple() {
use crate::Parser;
let mut parser = Parser::new("(1, 2, 3)");
let result = parser.parse();
assert!(result.is_ok());
}
#[test]
fn test_parse_object_keyword_keys() {
use crate::Parser;
let mut parser = Parser::new("{ if: 1, for: 2, let: 3 }");
let result = parser.parse();
assert!(result.is_ok(), "Object with keyword keys should parse");
}
#[test]
fn test_parse_object_spread() {
use crate::Parser;
let mut parser = Parser::new("{ ...other, x: 1 }");
let result = parser.parse();
assert!(result.is_ok(), "Object spread should parse");
}
}