use crate::SyntaxKind::{
BACKSLASH, BLOCK_COMMENT, CHOICE, CHOICE_BRACKET_CONTENT, CHOICE_BULLETS, CHOICE_CONDITION,
CHOICE_INNER_CONTENT, CHOICE_START_CONTENT, DIVERT, EOF, ESCAPE, GLUE, GLUE_NODE, HASH, IDENT,
IDENTIFIER, L_BRACE, L_BRACKET, L_PAREN, LABEL, LINE_COMMENT, MINUS, NEWLINE, PIPE, PLUS,
R_BRACE, R_BRACKET, R_PAREN, STAR, TAG, TAGS, TEXT, THREAD, TUNNEL_ONWARDS, WHITESPACE,
};
use super::Parser;
pub(crate) fn choice(p: &mut Parser<'_, '_>) {
p.start_node(CHOICE);
choice_bullets(p);
p.skip_ws();
if p.current() == L_PAREN && p.nth(1) == IDENT && p.nth(2) == R_PAREN {
label(p);
p.skip_ws();
if p.current() == NEWLINE && !matches!(p.nth(1), NEWLINE | EOF) {
p.bump(); }
}
while p.current() == L_BRACE {
choice_condition(p);
p.skip_ws();
}
while p.current() == NEWLINE && !matches!(p.nth(1), NEWLINE | EOF | STAR | PLUS | MINUS) {
p.bump(); p.skip_ws();
while p.current() == L_BRACE {
choice_condition(p);
p.skip_ws();
}
if at_choice_content(p) || p.current() == L_BRACKET {
break;
}
}
if at_choice_content(p) && p.current() != L_BRACKET {
choice_start_content(p);
}
if p.current() == HASH {
choice_tags(p);
}
if p.current() == L_BRACKET {
choice_bracket_content(p);
}
if at_choice_content(p) || p.nth_raw(0) == WHITESPACE {
choice_inner_content(p);
}
if p.current() == HASH {
choice_tags(p);
}
p.skip_ws();
if super::divert::at_divert(p) {
super::divert::divert(p);
}
if p.current() == HASH {
choice_tags(p);
}
if p.at(NEWLINE) {
p.bump();
} else if !p.at_eof() {
p.error("expected newline after choice".into());
}
p.finish_node();
}
fn choice_bullets(p: &mut Parser<'_, '_>) {
p.start_node(CHOICE_BULLETS);
while matches!(p.current(), STAR | PLUS) {
p.bump();
p.skip_ws();
}
p.finish_node();
}
pub(crate) fn label(p: &mut Parser<'_, '_>) {
p.start_node(LABEL);
p.bump(); p.skip_ws();
p.start_node(IDENTIFIER);
p.expect(IDENT);
p.finish_node();
p.skip_ws();
p.expect(R_PAREN);
p.finish_node();
}
fn choice_condition(p: &mut Parser<'_, '_>) {
p.start_node(CHOICE_CONDITION);
p.bump(); p.skip_ws();
super::expression::expression(p);
p.skip_ws();
p.expect(R_BRACE);
p.finish_node();
}
fn choice_start_content(p: &mut Parser<'_, '_>) {
p.start_node(CHOICE_START_CONTENT);
choice_content_elements(p);
p.finish_node();
}
fn choice_bracket_content(p: &mut Parser<'_, '_>) {
p.start_node(CHOICE_BRACKET_CONTENT);
p.bump(); while p.nth_raw(0) != R_BRACKET && !matches!(p.nth_raw(0), NEWLINE | EOF) {
if p.current() == HASH {
choice_tags(p);
continue;
}
let before = p.pos();
choice_content_element(p);
if p.pos() == before {
p.bump();
}
}
if p.nth_raw(0) == R_BRACKET {
p.bump();
} else {
p.error("expected `]`".into());
}
p.finish_node();
}
fn choice_inner_content(p: &mut Parser<'_, '_>) {
p.start_node(CHOICE_INNER_CONTENT);
choice_content_elements(p);
p.finish_node();
}
fn choice_content_elements(p: &mut Parser<'_, '_>) {
while (at_choice_content(p) || p.nth_raw(0) == WHITESPACE) && p.nth_raw(0) != L_BRACKET {
let before = p.pos();
choice_content_element(p);
if p.pos() == before {
break;
}
}
}
fn choice_content_element(p: &mut Parser<'_, '_>) {
if p.nth_raw(0) == WHITESPACE {
choice_text(p);
return;
}
match p.current() {
L_BRACE => {
super::inline::inline_logic(p);
}
GLUE => {
p.skip_ws();
p.start_node(GLUE_NODE);
p.bump();
p.finish_node();
}
BACKSLASH if !matches!(p.nth(1), NEWLINE | EOF) => {
p.start_node(ESCAPE);
p.bump(); p.bump(); p.finish_node();
}
_ => {
choice_text(p);
}
}
}
fn choice_text(p: &mut Parser<'_, '_>) {
p.start_node(TEXT);
loop {
if p.at_eof() {
break;
}
match p.nth_raw(0) {
NEWLINE | L_BRACKET | R_BRACKET | L_BRACE | R_BRACE | GLUE | DIVERT
| TUNNEL_ONWARDS | LINE_COMMENT | BLOCK_COMMENT | HASH | BACKSLASH | EOF | THREAD => {
break;
}
_ => p.bump(),
}
}
p.finish_node();
}
fn at_choice_content(p: &Parser<'_, '_>) -> bool {
!matches!(
p.current(),
NEWLINE | EOF | HASH | DIVERT | TUNNEL_ONWARDS | THREAD | PIPE | R_BRACE
)
}
fn choice_tags(p: &mut Parser<'_, '_>) {
p.start_node(TAGS);
while p.current() == HASH {
choice_tag(p);
}
p.finish_node();
}
fn choice_tag(p: &mut Parser<'_, '_>) {
p.start_node(TAG);
p.skip_ws();
p.bump_assert(HASH);
loop {
if p.at_eof() {
break;
}
match p.nth_raw(0) {
HASH | NEWLINE | EOF | L_BRACKET | R_BRACKET | DIVERT | TUNNEL_ONWARDS => break,
L_BRACE => {
super::inline::inline_logic(p);
}
_ => p.bump(),
}
}
p.finish_node();
}