use crate::SyntaxKind::{
self, ALTERNATION_BLOCK, ALTERNATION_MARKER, AMP, BANG, COMMA, CONDITIONAL_BLOCK, ELSE_BRANCH,
ENTRY, EOF, FAT_ARROW, IF_ARM, KW_ELSE, KW_IF, KW_MATCH, L_BRACE, MATCH_ARM, MATCH_PATTERN,
MINUS, NEWLINE, PIPE, QUESTION, R_BRACE, TILDE,
};
use super::Parser;
pub(crate) fn at_choice_point(p: &Parser<'_, '_>) -> bool {
p.at(L_BRACE) && p.nth(1) == QUESTION
}
pub(crate) fn at_conditional(p: &Parser<'_, '_>) -> bool {
p.at(L_BRACE) && matches!(p.nth(1), KW_IF | KW_MATCH)
}
pub(crate) fn at_alternation(p: &Parser<'_, '_>) -> bool {
p.at(L_BRACE) && matches!(p.nth(1), TILDE | AMP | BANG | PIPE)
}
pub(crate) fn conditional_block(p: &mut Parser<'_, '_>) {
p.start_node(CONDITIONAL_BLOCK);
if !p.enter_depth() {
p.expect(L_BRACE);
p.finish_node();
return;
}
p.expect(L_BRACE);
if p.eat(KW_IF) {
super::expr::expression(p);
if_arm(p);
if at_else_arm(p) {
else_branch(p);
}
} else if p.eat(KW_MATCH) {
super::expr::expression(p);
match_arm_list(p);
} else {
p.error("expected `if` or `match` after `{`".into());
}
p.expect(R_BRACE);
p.exit_depth();
p.finish_node();
}
fn if_arm(p: &mut Parser<'_, '_>) {
p.start_node(IF_ARM);
arm_body(p);
p.finish_node();
}
fn at_else_arm(p: &Parser<'_, '_>) -> bool {
p.at(KW_ELSE) && matches!(p.nth(1), L_BRACE | SyntaxKind::COLON)
}
fn else_branch(p: &mut Parser<'_, '_>) {
p.start_node(ELSE_BRANCH);
p.expect(KW_ELSE);
arm_body(p);
p.finish_node();
}
fn arm_body(p: &mut Parser<'_, '_>) {
if p.eat(SyntaxKind::COLON) {
colon_body(p);
} else if p.at(L_BRACE) {
super::block::braced_item_list(p, crate::SyntaxKind::BLOCK);
} else {
p.error("expected `:` or `{` to open the arm body".into());
}
}
fn colon_body(p: &mut Parser<'_, '_>) {
loop {
p.skip_ws();
if p.at(R_BRACE) || p.at_eof() || at_else_arm(p) {
break;
}
let before = p.pos();
super::block::body_line(p);
if p.pos() == before {
p.error_recover("unexpected token in conditional body");
}
}
}
fn match_arm_list(p: &mut Parser<'_, '_>) {
p.expect(L_BRACE);
if p.enter_depth() {
p.skip_ws_and_newlines();
loop {
if p.peek_skip_nl() == R_BRACE || p.at_eof() {
break;
}
let before = p.pos();
match_arm(p);
if p.pos() == before {
p.error_recover("unexpected token in match arm list");
p.skip_ws_and_newlines();
continue;
}
p.skip_ws_and_newlines();
p.eat(COMMA);
p.skip_ws_and_newlines();
}
p.exit_depth();
}
p.expect(R_BRACE);
}
fn match_arm(p: &mut Parser<'_, '_>) {
p.start_node(MATCH_ARM);
p.start_node(MATCH_PATTERN);
super::expr::expression(p);
p.finish_node();
p.expect(FAT_ARROW);
if p.at(L_BRACE) {
super::block::braced_item_list(p, crate::SyntaxKind::BLOCK);
} else {
super::expr::expression(p);
}
p.finish_node();
}
pub(crate) fn alternation_block(p: &mut Parser<'_, '_>) {
p.start_node(ALTERNATION_BLOCK);
if !p.enter_depth() {
p.expect(L_BRACE);
p.finish_node();
return;
}
p.expect(L_BRACE);
alternation_marker(p);
if is_multiline(p) {
multiline_entries(p);
} else {
inline_alternatives(p);
}
p.expect(R_BRACE);
p.exit_depth();
p.finish_node();
}
fn alternation_marker(p: &mut Parser<'_, '_>) {
p.start_node(ALTERNATION_MARKER);
p.skip_ws();
p.bump();
p.finish_node();
}
fn is_multiline(p: &Parser<'_, '_>) -> bool {
peek_is_newline(p, 0)
}
pub(crate) fn peek_is_newline(p: &Parser<'_, '_>, mut i: usize) -> bool {
loop {
match p.nth_raw(i) {
SyntaxKind::WHITESPACE | SyntaxKind::LINE_COMMENT | SyntaxKind::BLOCK_COMMENT => {
i += 1;
}
NEWLINE => return true,
_ => return false,
}
}
}
fn multiline_entries(p: &mut Parser<'_, '_>) {
loop {
p.skip_ws();
if p.at(R_BRACE) || p.at_eof() {
break;
}
if p.at(MINUS) {
entry(p);
} else {
let before = p.pos();
super::block::body_line(p);
if p.pos() == before {
p.error_recover("unexpected token before the first `-` entry");
}
}
}
}
fn entry(p: &mut Parser<'_, '_>) {
p.start_node(ENTRY);
p.expect(MINUS);
loop {
p.skip_ws();
if p.at(R_BRACE) || p.at(MINUS) || p.at_eof() {
break;
}
let before = p.pos();
super::block::body_line(p);
if p.pos() == before {
p.error_recover("unexpected token in entry");
}
}
p.finish_node();
}
fn inline_alternatives(p: &mut Parser<'_, '_>) {
loop {
p.skip_ws();
match p.current() {
R_BRACE | EOF | NEWLINE => break,
PIPE => {
p.bump();
}
SyntaxKind::HASH => super::content::tag_line_tail(p),
_ => super::content::content_items_until(p, &[PIPE, R_BRACE, NEWLINE]),
}
}
}