use crate::parser::context::ParserCtx;
use crate::parser::diagnostics::{DiagnosticKind, ParseDiagnostic, push_diagnostic};
use crate::parser::events::{Event, ExprParse, push_range};
use crate::parser::expr::{
parse_block_stmt, parse_expr, parse_for_specs, parse_name_signature_expr, parse_paren,
parse_prefix_interpolation, parse_quote_sym, parse_signature_expr, push_var_macro_name,
};
use crate::parser::lexer::{TokKind, Token};
use crate::syntax::SyntaxKind;
const IF_TERMINATORS: &[TokKind] = &[TokKind::EndKw, TokKind::ElseifKw, TokKind::ElseKw];
const END_ONLY: &[TokKind] = &[TokKind::EndKw];
const TRY_TERMINATORS: &[TokKind] = &[
TokKind::EndKw,
TokKind::CatchKw,
TokKind::ElseKw,
TokKind::FinallyKw,
];
pub(crate) fn parse_if_expr(
tokens: &[Token],
start: usize,
diagnostics: &mut Vec<ParseDiagnostic>,
) -> Option<ExprParse> {
let ctx = ParserCtx::new(tokens);
let mut events = vec![Event::Start(SyntaxKind::IF_EXPR), Event::Tok(start)];
let mut i = parse_condition(&ctx, &mut events, start + 1, diagnostics);
i = run_block(&ctx, &mut events, i, IF_TERMINATORS, diagnostics);
loop {
match ctx.token(i).map(|t| t.kind) {
Some(TokKind::ElseifKw) => {
events.push(Event::Start(SyntaxKind::ELSEIF_CLAUSE));
events.push(Event::Tok(i));
let cond_end = parse_condition(&ctx, &mut events, i + 1, diagnostics);
i = run_block(&ctx, &mut events, cond_end, IF_TERMINATORS, diagnostics);
events.push(Event::Finish);
}
Some(TokKind::ElseKw) => {
let after_else = ctx.skip_ws(i + 1);
if ctx.token(after_else).map(|t| t.kind) == Some(TokKind::IfKw) {
let kw = &ctx.tokens()[start];
push_diagnostic(
diagnostics,
DiagnosticKind::ElseIf,
"`else if` should be `elseif`",
kw.start,
kw.end,
);
events.push(Event::Start(SyntaxKind::ELSEIF_CLAUSE));
events.push(Event::Tok(i));
push_range(&mut events, i + 1, after_else);
events.push(Event::Tok(after_else));
let cond_end = parse_condition(&ctx, &mut events, after_else + 1, diagnostics);
i = run_block(&ctx, &mut events, cond_end, IF_TERMINATORS, diagnostics);
events.push(Event::Finish);
continue;
}
events.push(Event::Start(SyntaxKind::ELSE_CLAUSE));
events.push(Event::Tok(i));
i = run_block(&ctx, &mut events, i + 1, END_ONLY, diagnostics);
events.push(Event::Finish);
break;
}
_ => break,
}
}
i = expect_end(&ctx, &mut events, i, start, diagnostics);
events.push(Event::Finish);
Some(ExprParse {
start,
end: i,
events,
})
}
pub(crate) fn parse_function_expr(
tokens: &[Token],
start: usize,
diagnostics: &mut Vec<ParseDiagnostic>,
) -> Option<ExprParse> {
parse_function_like(tokens, start, SyntaxKind::FUNCTION_DEF, diagnostics)
}
pub(crate) fn parse_macro_def(
tokens: &[Token],
start: usize,
diagnostics: &mut Vec<ParseDiagnostic>,
) -> Option<ExprParse> {
parse_function_like(tokens, start, SyntaxKind::MACRO_DEF, diagnostics)
}
fn parse_function_like(
tokens: &[Token],
start: usize,
node_kind: SyntaxKind,
diagnostics: &mut Vec<ParseDiagnostic>,
) -> Option<ExprParse> {
let ctx = ParserCtx::new(tokens);
let mut events = vec![Event::Start(node_kind), Event::Tok(start)];
let sig_start = ctx.skip_ws_and_newlines(start + 1);
let mut i = if let Some(sig) = parse_signature_expr(tokens, sig_start, diagnostics) {
push_range(&mut events, start + 1, sig.start);
events.push(Event::Start(SyntaxKind::SIGNATURE));
let mut sig_events = sig.events;
if node_kind == SyntaxKind::FUNCTION_DEF {
match sig_events.first() {
Some(Event::Start(SyntaxKind::PAREN_EXPR))
if !signature_eventually_call(&sig_events, tokens) =>
{
sig_events[0] = Event::Start(SyntaxKind::TUPLE_EXPR);
}
Some(Event::Start(SyntaxKind::PAREN_BLOCK)) => {
sig_events[0] = Event::Start(SyntaxKind::TUPLE_EXPR);
}
_ => {}
}
}
events.extend(sig_events);
events.push(Event::Finish);
sig.end
} else {
start + 1
};
i = run_block(&ctx, &mut events, i, END_ONLY, diagnostics);
i = expect_end(&ctx, &mut events, i, start, diagnostics);
events.push(Event::Finish);
Some(ExprParse {
start,
end: i,
events,
})
}
pub(crate) fn parse_begin_expr(
tokens: &[Token],
start: usize,
diagnostics: &mut Vec<ParseDiagnostic>,
) -> Option<ExprParse> {
parse_block_only(tokens, start, SyntaxKind::BEGIN_EXPR, diagnostics)
}
pub(crate) fn parse_quote_expr(
tokens: &[Token],
start: usize,
diagnostics: &mut Vec<ParseDiagnostic>,
) -> Option<ExprParse> {
parse_block_only(tokens, start, SyntaxKind::QUOTE_EXPR, diagnostics)
}
fn parse_block_only(
tokens: &[Token],
start: usize,
node_kind: SyntaxKind,
diagnostics: &mut Vec<ParseDiagnostic>,
) -> Option<ExprParse> {
let ctx = ParserCtx::new(tokens);
let mut events = vec![Event::Start(node_kind), Event::Tok(start)];
let mut i = run_block(&ctx, &mut events, start + 1, END_ONLY, diagnostics);
i = expect_end(&ctx, &mut events, i, start, diagnostics);
events.push(Event::Finish);
Some(ExprParse {
start,
end: i,
events,
})
}
pub(crate) fn parse_while_expr(
tokens: &[Token],
start: usize,
diagnostics: &mut Vec<ParseDiagnostic>,
) -> Option<ExprParse> {
let ctx = ParserCtx::new(tokens);
let mut events = vec![Event::Start(SyntaxKind::WHILE_EXPR), Event::Tok(start)];
let mut i = parse_condition(&ctx, &mut events, start + 1, diagnostics);
i = run_block(&ctx, &mut events, i, END_ONLY, diagnostics);
i = expect_end(&ctx, &mut events, i, start, diagnostics);
events.push(Event::Finish);
Some(ExprParse {
start,
end: i,
events,
})
}
pub(crate) fn parse_for_expr(
tokens: &[Token],
start: usize,
diagnostics: &mut Vec<ParseDiagnostic>,
) -> Option<ExprParse> {
let ctx = ParserCtx::new(tokens);
let mut events = vec![Event::Start(SyntaxKind::FOR_EXPR), Event::Tok(start)];
let mut i = parse_header(
&ctx,
&mut events,
start + 1,
SyntaxKind::FOR_BINDING,
true,
diagnostics,
);
i = run_block(&ctx, &mut events, i, END_ONLY, diagnostics);
i = expect_end(&ctx, &mut events, i, start, diagnostics);
events.push(Event::Finish);
Some(ExprParse {
start,
end: i,
events,
})
}
pub(crate) fn parse_let_expr(
tokens: &[Token],
start: usize,
diagnostics: &mut Vec<ParseDiagnostic>,
) -> Option<ExprParse> {
let ctx = ParserCtx::new(tokens);
let mut events = vec![Event::Start(SyntaxKind::LET_EXPR), Event::Tok(start)];
let mut i = parse_header(
&ctx,
&mut events,
start + 1,
SyntaxKind::LET_BINDINGS,
true,
diagnostics,
);
i = run_block(&ctx, &mut events, i, END_ONLY, diagnostics);
i = expect_end(&ctx, &mut events, i, start, diagnostics);
events.push(Event::Finish);
Some(ExprParse {
start,
end: i,
events,
})
}
pub(crate) fn parse_try_expr(
tokens: &[Token],
start: usize,
diagnostics: &mut Vec<ParseDiagnostic>,
) -> Option<ExprParse> {
let ctx = ParserCtx::new(tokens);
let mut events = vec![Event::Start(SyntaxKind::TRY_EXPR), Event::Tok(start)];
let mut i = run_block(&ctx, &mut events, start + 1, TRY_TERMINATORS, diagnostics);
let mut saw_handler = false;
let mut saw_catch = false;
loop {
match ctx.token(i).map(|t| t.kind) {
Some(TokKind::CatchKw) => {
saw_handler = true;
saw_catch = true;
events.push(Event::Start(SyntaxKind::CATCH_CLAUSE));
events.push(Event::Tok(i));
let var_start = ctx.skip_ws(i + 1);
push_range(&mut events, i + 1, var_start);
let mut j = var_start;
if !header_ends(&ctx, var_start)
&& let Some(var) = parse_expr(tokens, var_start, 0, diagnostics)
{
events.extend(var.events);
j = var.end;
}
i = run_block(&ctx, &mut events, j, TRY_TERMINATORS, diagnostics);
events.push(Event::Finish);
}
Some(TokKind::ElseKw) => {
events.push(Event::Start(SyntaxKind::ELSE_CLAUSE));
events.push(Event::Tok(i));
if saw_catch {
i = run_block(&ctx, &mut events, i + 1, TRY_TERMINATORS, diagnostics);
} else {
let kw = &ctx.tokens()[i];
push_diagnostic(
diagnostics,
DiagnosticKind::ElseWithoutCatch,
"`else` without `catch`",
kw.start,
kw.end,
);
events.push(Event::Start(SyntaxKind::ERROR));
i = run_block(&ctx, &mut events, i + 1, TRY_TERMINATORS, diagnostics);
events.push(Event::Finish);
}
events.push(Event::Finish);
}
Some(TokKind::FinallyKw) => {
saw_handler = true;
events.push(Event::Start(SyntaxKind::FINALLY_CLAUSE));
events.push(Event::Tok(i));
i = run_block(&ctx, &mut events, i + 1, TRY_TERMINATORS, diagnostics);
events.push(Event::Finish);
if ctx.token(i).map(|t| t.kind) != Some(TokKind::CatchKw) {
break;
}
}
_ => break,
}
}
if !saw_handler {
let kw = &ctx.tokens()[start];
push_diagnostic(
diagnostics,
DiagnosticKind::MissingTryHandler,
"expected `catch` or `finally`",
kw.start,
kw.end,
);
}
i = expect_end(&ctx, &mut events, i, start, diagnostics);
events.push(Event::Finish);
Some(ExprParse {
start,
end: i,
events,
})
}
pub(crate) fn parse_struct_expr(
tokens: &[Token],
start: usize,
diagnostics: &mut Vec<ParseDiagnostic>,
) -> Option<ExprParse> {
let ctx = ParserCtx::new(tokens);
let mut events = vec![Event::Start(SyntaxKind::STRUCT_DEF)];
let mut i = start;
if ctx.token(i).map(|t| t.kind) == Some(TokKind::MutableKw) {
events.push(Event::Tok(i));
let next = ctx.skip_ws(i + 1);
push_range(&mut events, i + 1, next);
i = next;
}
if ctx.token(i).map(|t| t.kind) == Some(TokKind::StructKw) {
events.push(Event::Tok(i));
i += 1;
} else {
let kw = &ctx.tokens()[start];
push_diagnostic(
diagnostics,
DiagnosticKind::MissingStruct,
"expected `struct`",
kw.start,
kw.end,
);
}
i = parse_signature(&ctx, &mut events, i, diagnostics);
i = run_block(&ctx, &mut events, i, END_ONLY, diagnostics);
i = expect_end(&ctx, &mut events, i, start, diagnostics);
events.push(Event::Finish);
Some(ExprParse {
start,
end: i,
events,
})
}
fn skip_trivia_and_semis(ctx: &ParserCtx<'_>, events: &mut Vec<Event>, mut i: usize) -> usize {
loop {
let next = ctx.skip_trivia(i);
push_range(events, i, next);
i = next;
if ctx.token(i).map(|t| t.kind) == Some(TokKind::Semicolon) {
events.push(Event::Tok(i));
i += 1;
} else {
return i;
}
}
}
pub(crate) fn parse_abstract_type(
tokens: &[Token],
start: usize,
diagnostics: &mut Vec<ParseDiagnostic>,
) -> Option<ExprParse> {
let ctx = ParserCtx::new(tokens);
let mut events = vec![Event::Start(SyntaxKind::ABSTRACT_DEF), Event::Tok(start)];
let type_idx = ctx.skip_trivia(start + 1);
push_range(&mut events, start + 1, type_idx);
events.push(Event::Tok(type_idx));
let spec_start = ctx.skip_trivia(type_idx + 1);
push_range(&mut events, type_idx + 1, spec_start);
let mut i = spec_start;
if ctx.token(spec_start).map(|t| t.kind) != Some(TokKind::EndKw)
&& let Some(expr) = parse_expr(tokens, spec_start, 0, diagnostics)
{
events.push(Event::Start(SyntaxKind::SIGNATURE));
events.extend(expr.events);
events.push(Event::Finish);
i = expr.end;
}
let before_end = skip_trivia_and_semis(&ctx, &mut events, i);
i = expect_end(&ctx, &mut events, before_end, start, diagnostics);
events.push(Event::Finish);
Some(ExprParse {
start,
end: i,
events,
})
}
pub(crate) fn parse_primitive_type(
tokens: &[Token],
start: usize,
diagnostics: &mut Vec<ParseDiagnostic>,
) -> Option<ExprParse> {
let ctx = ParserCtx::new(tokens);
let mut events = vec![Event::Start(SyntaxKind::PRIMITIVE_DEF), Event::Tok(start)];
let type_idx = ctx.skip_trivia(start + 1);
push_range(&mut events, start + 1, type_idx);
events.push(Event::Tok(type_idx));
let spec_start = ctx.skip_trivia(type_idx + 1);
push_range(&mut events, type_idx + 1, spec_start);
let mut i = spec_start;
if ctx.token(spec_start).map(|t| t.kind) != Some(TokKind::EndKw)
&& let Some(expr) = parse_expr(tokens, spec_start, 0, diagnostics)
{
events.push(Event::Start(SyntaxKind::SIGNATURE));
events.extend(expr.events);
events.push(Event::Finish);
i = expr.end;
}
let size_start = ctx.skip_trivia(i);
push_range(&mut events, i, size_start);
i = size_start;
if ctx.token(size_start).map(|t| t.kind) != Some(TokKind::EndKw)
&& let Some(size) = parse_expr(tokens, size_start, 0, diagnostics)
{
events.extend(size.events);
i = size.end;
}
let before_end = skip_trivia_and_semis(&ctx, &mut events, i);
i = expect_end(&ctx, &mut events, before_end, start, diagnostics);
events.push(Event::Finish);
Some(ExprParse {
start,
end: i,
events,
})
}
pub(crate) fn parse_module_expr(
tokens: &[Token],
start: usize,
diagnostics: &mut Vec<ParseDiagnostic>,
) -> Option<ExprParse> {
let ctx = ParserCtx::new(tokens);
let mut events = vec![Event::Start(SyntaxKind::MODULE_DEF), Event::Tok(start)];
let mut i = parse_signature(&ctx, &mut events, start + 1, diagnostics);
i = run_module_block(&ctx, &mut events, i, END_ONLY, diagnostics);
i = expect_end(&ctx, &mut events, i, start, diagnostics);
events.push(Event::Finish);
Some(ExprParse {
start,
end: i,
events,
})
}
pub(crate) enum KwStmt {
Bare,
ExprTuple,
}
pub(crate) fn parse_keyword_stmt(
tokens: &[Token],
start: usize,
node_kind: SyntaxKind,
body: KwStmt,
optional_value: bool,
diagnostics: &mut Vec<ParseDiagnostic>,
) -> Option<ExprParse> {
let ctx = ParserCtx::new(tokens);
let mut events = vec![Event::Start(node_kind), Event::Tok(start)];
let mut i = start + 1;
if !matches!(body, KwStmt::Bare) {
let operand_start = ctx.skip_ws(i);
if optional_value
&& ctx
.token(operand_start)
.is_some_and(|t| is_close_delimiter_tok(t.kind))
{
events.push(Event::Finish);
return Some(ExprParse {
start,
end: i,
events,
});
}
push_range(&mut events, i, operand_start);
i = operand_start;
if !header_ends(&ctx, i) {
let operand = match body {
KwStmt::ExprTuple => parse_block_stmt(tokens, i, false, diagnostics),
_ => None,
};
if let Some(expr) = operand {
events.extend(expr.events);
i = expr.end;
}
}
while !header_ends(&ctx, i) {
match ctx.token(i).map(|t| t.kind) {
Some(TokKind::Dollar) => {
let interp = parse_prefix_interpolation(&ctx, i, diagnostics);
events.extend(interp.events);
i = interp.end;
}
Some(TokKind::At) => {
i = push_macro_name(&ctx, &mut events, i, diagnostics);
}
_ => {
events.push(Event::Tok(i));
i += 1;
}
}
}
}
events.push(Event::Finish);
Some(ExprParse {
start,
end: i,
events,
})
}
pub(crate) fn parse_name_list_stmt(
tokens: &[Token],
start: usize,
node_kind: SyntaxKind,
diagnostics: &mut Vec<ParseDiagnostic>,
) -> Option<ExprParse> {
let ctx = ParserCtx::new(tokens);
let mut events = vec![Event::Start(node_kind), Event::Tok(start)];
let is_public = node_kind == SyntaxKind::PUBLIC_STMT;
let mut i = ctx.skip_ws_and_newlines(start + 1);
push_range(&mut events, start + 1, i);
while !header_ends(&ctx, i) {
let mut consumed_name = false;
match ctx.token(i).map(|t| t.kind) {
Some(TokKind::Dollar) => {
let interp = parse_prefix_interpolation(&ctx, i, diagnostics);
events.extend(interp.events);
i = interp.end;
consumed_name = true;
}
Some(TokKind::At) => {
i = push_macro_name(&ctx, &mut events, i, diagnostics);
consumed_name = true;
}
Some(TokKind::LParen) if !is_public => {
if let Some(item) = parse_paren(&ctx, i, false, diagnostics) {
events.extend(item.events);
i = item.end;
consumed_name = true;
} else {
events.push(Event::Tok(i));
i += 1;
consumed_name = true;
}
}
Some(TokKind::Comma) => {
events.push(Event::Tok(i));
let next = ctx.skip_ws_and_newlines(i + 1);
push_range(&mut events, i + 1, next);
i = next;
}
_ => {
let is_ws = ctx.token(i).is_some_and(|t| t.kind.is_trivia());
events.push(Event::Tok(i));
i += 1;
consumed_name = !is_ws;
}
}
if is_public && consumed_name {
let sep = ctx.skip_ws(i);
if !matches!(ctx.token(sep).map(|t| t.kind), Some(TokKind::Comma)) {
break;
}
}
}
events.push(Event::Finish);
Some(ExprParse {
start,
end: i,
events,
})
}
pub(crate) fn parse_import_stmt(
tokens: &[Token],
start: usize,
node_kind: SyntaxKind,
diagnostics: &mut Vec<ParseDiagnostic>,
) -> Option<ExprParse> {
let ctx = ParserCtx::new(tokens);
let mut events = vec![Event::Start(node_kind), Event::Tok(start)];
let is_using = node_kind == SyntaxKind::USING_STMT;
let valid_colon = {
let (mut probe, mut probe_diags) = (Vec::new(), Vec::new());
let base_end = parse_import_clause(&ctx, &mut probe, start + 1, 0, &mut probe_diags);
let sep = ctx.skip_ws(base_end);
ctx.token(sep).map(|t| t.kind) == Some(TokKind::Colon)
};
let base_wraps = usize::from(is_using) + usize::from(valid_colon);
let mut i = parse_import_clause(&ctx, &mut events, start + 1, base_wraps, diagnostics);
let mut valid_colon_consumed = false;
let mut first_sep = true;
loop {
let sep = ctx.skip_ws(i);
match ctx.token(sep).map(|t| t.kind) {
Some(kind @ (TokKind::Comma | TokKind::Colon)) => {
push_range(&mut events, i, sep);
events.push(Event::Tok(sep));
let is_valid_colon = kind == TokKind::Colon && first_sep && valid_colon;
first_sep = false;
if is_valid_colon {
valid_colon_consumed = true;
i = parse_import_clause(&ctx, &mut events, sep + 1, 0, diagnostics);
} else if kind == TokKind::Colon {
events.push(Event::Start(SyntaxKind::ERROR));
i = parse_import_clause(&ctx, &mut events, sep + 1, 0, diagnostics);
events.push(Event::Finish);
if i > sep + 1 {
let last = &ctx.tokens()[i - 1];
push_diagnostic(
diagnostics,
DiagnosticKind::ImportRecoveryColon,
"recovery `:` in import",
last.start,
last.end,
);
}
break;
} else {
let wraps = usize::from(is_using && !valid_colon_consumed);
i = parse_import_clause(&ctx, &mut events, sep + 1, wraps, diagnostics);
}
}
_ => break,
}
}
while !header_ends(&ctx, i) {
events.push(Event::Tok(i));
i += 1;
}
events.push(Event::Finish);
Some(ExprParse {
start,
end: i,
events,
})
}
fn parse_import_clause(
ctx: &ParserCtx<'_>,
events: &mut Vec<Event>,
after_sep: usize,
error_wraps: usize,
diagnostics: &mut Vec<ParseDiagnostic>,
) -> usize {
let path_start = ctx.skip_trivia(after_sep);
let mut path_events = Vec::new();
let path_end = parse_import_path(ctx, &mut path_events, path_start, diagnostics);
if path_end == path_start {
return after_sep;
}
push_range(events, after_sep, path_start);
let as_idx = ctx.skip_ws(path_end);
if is_as_kw(ctx, as_idx) {
let alias_start = ctx.skip_ws(as_idx + 1);
if matches!(ctx.token(alias_start).map(|t| t.kind), Some(TokKind::Ident)) {
for _ in 0..error_wraps {
events.push(Event::Start(SyntaxKind::ERROR));
}
events.push(Event::Start(SyntaxKind::IMPORT_ALIAS));
events.extend(path_events);
push_range(events, path_end, as_idx);
events.push(Event::Tok(as_idx)); push_range(events, as_idx + 1, alias_start);
events.push(Event::Tok(alias_start)); events.push(Event::Finish);
for _ in 0..error_wraps {
events.push(Event::Finish);
}
return alias_start + 1;
}
}
events.extend(path_events);
path_end
}
fn push_macro_name(
ctx: &ParserCtx<'_>,
events: &mut Vec<Event>,
at_idx: usize,
diagnostics: &mut Vec<ParseDiagnostic>,
) -> usize {
events.push(Event::Start(SyntaxKind::MACRO_NAME));
events.push(Event::Tok(at_idx)); let mut i = at_idx + 1;
if let Some(end) = push_var_macro_name(ctx, events, i, diagnostics) {
i = end;
} else if ctx.token(i).map(|t| t.kind) == Some(TokKind::Ident) {
events.push(Event::Tok(i));
i += 1;
}
events.push(Event::Finish);
i
}
fn parse_import_path(
ctx: &ParserCtx<'_>,
events: &mut Vec<Event>,
start: usize,
diagnostics: &mut Vec<ParseDiagnostic>,
) -> usize {
let mut i = start;
let mut body = Vec::new();
loop {
let dot = ctx.skip_ws(i);
if matches!(
ctx.token(dot).map(|t| t.kind),
Some(TokKind::Dot | TokKind::DotDot | TokKind::DotDotDot)
) {
push_range(&mut body, i, dot);
body.push(Event::Tok(dot));
i = dot + 1;
} else {
break;
}
}
match ctx.token(i).map(|t| t.kind) {
Some(TokKind::Ident) => {
body.push(Event::Tok(i));
i += 1;
}
Some(TokKind::Dollar) => {
let interp = parse_prefix_interpolation(ctx, i, diagnostics);
body.extend(interp.events);
i = interp.end;
}
Some(TokKind::At) => {
i = push_macro_name(ctx, &mut body, i, diagnostics);
}
Some(k) if is_op_name(k) || is_unicode_op_name(k) || is_dotted_op_name(k) => {
body.push(Event::Tok(i));
i += 1;
}
_ => {
if body.is_empty() {
return start;
}
events.push(Event::Start(SyntaxKind::IMPORT_PATH));
events.extend(body);
events.push(Event::Finish);
return i;
}
}
loop {
match (
ctx.token(i).map(|t| t.kind),
ctx.token(i + 1).map(|t| t.kind),
) {
(Some(TokKind::Dot), Some(TokKind::Ident)) => {
body.push(Event::Tok(i)); body.push(Event::Tok(i + 1)); i += 2;
}
(Some(TokKind::Dot), Some(TokKind::At)) => {
body.push(Event::Tok(i)); i = push_macro_name(ctx, &mut body, i + 1, diagnostics);
}
(Some(TokKind::Dot), Some(TokKind::Colon)) => {
let Some(quote) = parse_quote_sym(ctx, i + 1, diagnostics, false, false) else {
break;
};
body.push(Event::Tok(i)); body.extend(quote.events);
i = quote.end;
}
(Some(TokKind::Dot), Some(TokKind::LParen))
if ctx.token(i + 2).map(|t| t.kind) == Some(TokKind::Colon) =>
{
let Some(quote) = parse_quote_sym(ctx, i + 2, diagnostics, false, false) else {
break;
};
let rparen = quote.end;
if ctx.token(rparen).map(|t| t.kind) != Some(TokKind::RParen) {
break;
}
body.push(Event::Tok(i)); body.push(Event::Start(SyntaxKind::PAREN_EXPR));
body.push(Event::Tok(i + 1)); body.extend(quote.events);
body.push(Event::Tok(rparen)); body.push(Event::Finish); i = rparen + 1;
}
(Some(TokKind::DotDotDot), _) => {
body.push(Event::Tok(i));
i += 1;
}
(Some(k), _) if is_dotted_op_name(k) || is_unicode_op_name(k) => {
body.push(Event::Tok(i));
i += 1;
}
_ => break,
}
}
events.push(Event::Start(SyntaxKind::IMPORT_PATH));
events.extend(body);
events.push(Event::Finish);
i
}
pub(super) fn is_op_name(kind: TokKind) -> bool {
use TokKind::*;
matches!(
kind,
Plus | Minus
| Star
| Slash
| Backslash
| SlashSlash
| Caret
| Percent
| EqEq
| NotEq
| EqEqEq
| NotEqEq
| Lt
| Le
| Gt
| Ge
| AndAnd
| OrOr
| Subtype
| Supertype
| Arrow
| LongArrow
| LeftRightArrow
| LeftLongArrow
| FatArrow
| Shl
| Shr
| UShr
| PipeGt
| PipeLt
| Bang
| Amp
| Pipe
| Tilde
)
}
fn signature_eventually_call(events: &[Event], tokens: &[Token]) -> bool {
use SyntaxKind::*;
match events.first() {
Some(Event::Start(CALL_EXPR)) => true,
Some(Event::Start(TYPE_ANNOTATION | WHERE_EXPR | PAREN_EXPR)) => {
first_child_slice(events).is_some_and(|child| signature_eventually_call(child, tokens))
}
Some(Event::Start(BINARY_EXPR)) => {
direct_child_operator(events, tokens).is_some_and(is_call_infix_operator)
}
_ => false,
}
}
fn first_child_slice(events: &[Event]) -> Option<&[Event]> {
let mut depth = 0i32;
let mut start = None;
for (i, ev) in events.iter().enumerate() {
match ev {
Event::Start(_) => {
depth += 1;
if depth == 2 {
start = Some(i);
break;
}
}
Event::Finish => depth -= 1,
Event::Tok(_) => {}
}
}
let start = start?;
let mut depth = 0i32;
for (i, ev) in events[start..].iter().enumerate() {
match ev {
Event::Start(_) => depth += 1,
Event::Finish => {
depth -= 1;
if depth == 0 {
return Some(&events[start..=start + i]);
}
}
Event::Tok(_) => {}
}
}
None
}
fn direct_child_operator(events: &[Event], tokens: &[Token]) -> Option<TokKind> {
let mut depth = 0i32;
for ev in events {
match ev {
Event::Start(_) => depth += 1,
Event::Finish => depth -= 1,
Event::Tok(i) => {
let kind = tokens[*i].kind;
if depth == 1 && !kind.is_trivia() {
return Some(kind);
}
}
}
}
None
}
fn is_call_infix_operator(kind: TokKind) -> bool {
use TokKind::*;
matches!(
kind,
Plus | Minus
| Star
| Slash
| Backslash
| SlashSlash
| Caret
| Percent
| Colon
| DotDot
| FatArrow
| PipeGt
| PipeLt
| LeftRightArrow
| Shl
| Shr
| UShr
| Amp
| Pipe
| EqEq
| NotEq
| Lt
| Le
| Gt
| Ge
| Tilde
)
}
fn is_dotted_op_name(kind: TokKind) -> bool {
use TokKind::*;
matches!(
kind,
DotPlus
| DotMinus
| DotStar
| DotSlash
| DotSlashSlash
| DotCaret
| DotPercent
| DotEqEq
| DotNotEq
| DotEqEqEq
| DotNotEqEq
| DotLt
| DotLe
| DotGt
| DotGe
| DotAndAnd
| DotOrOr
| DotTilde
| DotFatArrow
| DotLongArrow
| DotPipeGt
)
}
fn is_unicode_op_name(kind: TokKind) -> bool {
use TokKind::*;
matches!(
kind,
UniArrow
| UniComparison
| UniColon
| UniPlus
| UniTimes
| UniPower
| UniAssign
| UniRadical
)
}
fn is_as_kw(ctx: &ParserCtx<'_>, i: usize) -> bool {
matches!(ctx.token(i), Some(t) if t.kind == TokKind::Ident && t.text == "as")
}
pub(crate) fn parse_do_block(
ctx: &ParserCtx<'_>,
lhs: ExprParse,
do_idx: usize,
diagnostics: &mut Vec<ParseDiagnostic>,
) -> ExprParse {
let mut events = vec![Event::Start(SyntaxKind::DO_EXPR)];
events.extend(lhs.events);
push_range(&mut events, lhs.end, do_idx);
events.push(Event::Tok(do_idx));
let mut i = parse_do_params(ctx, &mut events, do_idx + 1, diagnostics);
i = run_block(ctx, &mut events, i, END_ONLY, diagnostics);
i = expect_end(ctx, &mut events, i, do_idx, diagnostics);
events.push(Event::Finish);
ExprParse {
start: lhs.start,
end: i,
events,
}
}
fn parse_do_params(
ctx: &ParserCtx<'_>,
events: &mut Vec<Event>,
after_kw: usize,
diagnostics: &mut Vec<ParseDiagnostic>,
) -> usize {
let start = ctx.skip_ws(after_kw);
push_range(events, after_kw, start);
if header_ends(ctx, start) {
return start;
}
events.push(Event::Start(SyntaxKind::DO_PARAMS));
let mut i = match parse_expr(ctx.tokens(), start, 0, diagnostics) {
Some(expr) => {
events.extend(expr.events);
expr.end
}
None => start,
};
loop {
let next = ctx.skip_ws(i);
if ctx.token(next).map(|t| t.kind) != Some(TokKind::Comma) {
break;
}
push_range(events, i, next + 1);
let arg_start = ctx.skip_ws(next + 1);
push_range(events, next + 1, arg_start);
match parse_expr(ctx.tokens(), arg_start, 0, diagnostics) {
Some(expr) => {
events.extend(expr.events);
i = expr.end;
}
None => {
i = arg_start;
break;
}
}
}
events.push(Event::Finish);
i
}
fn parse_condition(
ctx: &ParserCtx<'_>,
events: &mut Vec<Event>,
after_kw: usize,
diagnostics: &mut Vec<ParseDiagnostic>,
) -> usize {
let cond_start = ctx.skip_ws(after_kw);
push_range(events, after_kw, cond_start);
match parse_expr(ctx.tokens(), cond_start, 0, diagnostics) {
Some(cond) => {
events.push(Event::Start(SyntaxKind::CONDITION));
events.extend(cond.events);
events.push(Event::Finish);
cond.end
}
None => {
let kw = &ctx.tokens()[after_kw - 1];
push_diagnostic(
diagnostics,
DiagnosticKind::MissingCondition,
"expected a condition",
kw.start,
kw.end,
);
cond_start
}
}
}
fn parse_signature(
ctx: &ParserCtx<'_>,
events: &mut Vec<Event>,
after_kw: usize,
diagnostics: &mut Vec<ParseDiagnostic>,
) -> usize {
let sig_start = ctx.skip_ws(after_kw);
push_range(events, after_kw, sig_start);
if header_ends(ctx, sig_start) {
return sig_start;
}
events.push(Event::Start(SyntaxKind::SIGNATURE));
let i = if let Some(expr) = parse_name_signature_expr(ctx.tokens(), sig_start, 0, diagnostics) {
events.extend(expr.events);
expr.end
} else if ctx.token(sig_start).map(|t| t.kind) == Some(TokKind::Dollar) {
let interp = parse_prefix_interpolation(ctx, sig_start, diagnostics);
events.extend(interp.events);
interp.end
} else {
sig_start
};
events.push(Event::Finish);
i
}
fn parse_header(
ctx: &ParserCtx<'_>,
events: &mut Vec<Event>,
after_kw: usize,
node_kind: SyntaxKind,
run_expr: bool,
diagnostics: &mut Vec<ParseDiagnostic>,
) -> usize {
let header_start = ctx.skip_ws(after_kw);
push_range(events, after_kw, header_start);
if header_ends(ctx, header_start) {
return header_start;
}
events.push(Event::Start(node_kind));
if node_kind == SyntaxKind::FOR_BINDING {
let i = parse_for_specs(ctx, header_start, events, false, diagnostics);
events.push(Event::Finish);
return i;
}
let mut i = header_start;
loop {
let binding = run_expr.then(|| parse_expr(ctx.tokens(), i, 0, diagnostics));
if let Some(Some(expr)) = binding {
events.extend(expr.events);
i = expr.end;
} else if ctx.token(i).map(|t| t.kind) == Some(TokKind::Dollar) {
let interp = parse_prefix_interpolation(ctx, i, diagnostics);
events.extend(interp.events);
i = interp.end;
}
let sep = ctx.skip_ws(i);
if ctx.token(sep).map(|t| t.kind) != Some(TokKind::Comma) {
break;
}
push_range(events, i, sep + 1);
i = sep + 1;
let next = ctx.skip_trivia(i);
if next > i && !header_ends(ctx, next) {
push_range(events, i, next);
i = next;
}
}
events.push(Event::Finish);
i
}
fn is_close_delimiter_tok(kind: TokKind) -> bool {
matches!(kind, TokKind::RParen | TokKind::RBracket | TokKind::RBrace)
}
fn header_ends(ctx: &ParserCtx<'_>, i: usize) -> bool {
match ctx.token(i).map(|t| t.kind) {
None => true,
Some(k) => {
matches!(k, TokKind::Newline | TokKind::Semicolon)
|| matches!(
k,
TokKind::EndKw
| TokKind::ElseifKw
| TokKind::ElseKw
| TokKind::CatchKw
| TokKind::FinallyKw
)
}
}
}
fn run_block(
ctx: &ParserCtx<'_>,
events: &mut Vec<Event>,
start: usize,
terminators: &[TokKind],
diagnostics: &mut Vec<ParseDiagnostic>,
) -> usize {
run_block_inner(ctx, events, start, terminators, false, diagnostics)
}
fn run_module_block(
ctx: &ParserCtx<'_>,
events: &mut Vec<Event>,
start: usize,
terminators: &[TokKind],
diagnostics: &mut Vec<ParseDiagnostic>,
) -> usize {
run_block_inner(ctx, events, start, terminators, true, diagnostics)
}
fn run_block_inner(
ctx: &ParserCtx<'_>,
events: &mut Vec<Event>,
start: usize,
terminators: &[TokKind],
public_context: bool,
diagnostics: &mut Vec<ParseDiagnostic>,
) -> usize {
let tokens = ctx.tokens();
events.push(Event::Start(SyntaxKind::BLOCK));
let mut i = start;
let mut parsed_any = false;
loop {
let mut saw_separator = false;
while let Some(k) = tokens.get(i).map(|t| t.kind) {
if k.is_trivia() || k == TokKind::Semicolon {
if matches!(k, TokKind::Newline | TokKind::Semicolon) {
saw_separator = true;
}
events.push(Event::Tok(i));
i += 1;
} else {
break;
}
}
match tokens.get(i).map(|t| t.kind) {
None => break,
Some(k) if terminators.contains(&k) => break,
Some(_) if parsed_any && !saw_separator => break,
Some(_) => {
let parsed = parse_block_stmt(tokens, i, public_context, diagnostics);
if let Some(stmt) = parsed {
events.extend(stmt.events);
i = stmt.end;
parsed_any = true;
} else {
events.push(Event::Tok(i));
i += 1;
}
}
}
}
events.push(Event::Finish);
i
}
fn expect_end(
ctx: &ParserCtx<'_>,
events: &mut Vec<Event>,
i: usize,
open_start: usize,
diagnostics: &mut Vec<ParseDiagnostic>,
) -> usize {
let mut i = i;
match ctx.token(i).map(|t| t.kind) {
Some(TokKind::EndKw) => {
events.push(Event::Tok(i));
i + 1
}
Some(k) if !is_block_junk_stopper(k) => {
i = collect_block_junk(ctx, events, i, diagnostics);
if ctx.token(i).map(|t| t.kind) == Some(TokKind::EndKw) {
events.push(Event::Tok(i));
i + 1
} else {
i
}
}
_ => {
let kw = &ctx.tokens()[open_start];
push_diagnostic(
diagnostics,
DiagnosticKind::MissingEnd,
"expected `end`",
kw.start,
kw.end,
);
i
}
}
}
fn is_block_junk_stopper(kind: TokKind) -> bool {
matches!(
kind,
TokKind::EndKw
| TokKind::ElseKw
| TokKind::ElseifKw
| TokKind::CatchKw
| TokKind::FinallyKw
| TokKind::RParen
| TokKind::RBracket
| TokKind::RBrace
)
}
fn collect_block_junk(
ctx: &ParserCtx<'_>,
events: &mut Vec<Event>,
mut i: usize,
diagnostics: &mut Vec<ParseDiagnostic>,
) -> usize {
let tokens = ctx.tokens();
let first = i;
events.push(Event::Start(SyntaxKind::ERROR));
while let Some(k) = tokens.get(i).map(|t| t.kind) {
if is_block_junk_stopper(k) {
break;
}
events.push(Event::Tok(i));
i += 1;
}
events.push(Event::Finish);
push_diagnostic(
diagnostics,
DiagnosticKind::TrailingJunk,
"trailing tokens after statement",
tokens[first].start,
tokens[first].end,
);
i
}