use crate::SyntaxKind::{
AMP, BACKSLASH, BANG, BLOCK_COMMENT, BRANCH_CONTENT, BRANCHLESS_COND_BODY, COLON,
CONDITIONAL_WITH_EXPR, DIVERT, DOLLAR, ELSE_BRANCH, EOF, ESCAPE, GLUE, GLUE_NODE, HASH,
IMPLICIT_SEQUENCE, INLINE_BRANCHES_COND, INLINE_BRANCHES_SEQ, INLINE_LOGIC, INNER_EXPRESSION,
KW_CYCLE, KW_ELSE, KW_ONCE, KW_SHUFFLE, KW_STOPPING, L_BRACE, L_PAREN, LINE_COMMENT, MINUS,
MULTILINE_BLOCK, MULTILINE_BRANCH_BODY, MULTILINE_BRANCH_COND, MULTILINE_BRANCH_SEQ,
MULTILINE_BRANCHES_COND, MULTILINE_BRANCHES_SEQ, MULTILINE_CONDITIONAL, NEWLINE, PIPE, PLUS,
R_BRACE, R_PAREN, SEQUENCE_SYMBOL_ANNOTATION, SEQUENCE_WITH_ANNOTATION,
SEQUENCE_WORD_ANNOTATION, STAR, TAG, TAGS, TEXT, THREAD, TILDE, TUNNEL_ONWARDS, WHITESPACE,
};
use super::Parser;
pub(crate) fn inline_logic(p: &mut Parser<'_, '_>) {
p.skip_ws(); p.start_node(INLINE_LOGIC);
let brace_pos = p.pos(); p.bump();
if p.at_depth_limit() {
p.error("nesting depth limit exceeded".into());
let mut depth = 1u32;
while !p.at_eof() && depth > 0 {
match p.current() {
L_BRACE => {
depth += 1;
p.bump();
}
R_BRACE => {
depth -= 1;
if depth > 0 {
p.bump();
}
}
_ => p.bump(),
}
}
if p.current() == R_BRACE {
p.bump();
}
p.finish_node();
return;
}
p.depth += 1;
p.skip_ws();
inner_logic(p, brace_pos);
p.skip_ws();
if p.current() == R_BRACE {
p.bump();
} else if !p.at_eof() {
p.error("expected `}`".into());
}
p.depth -= 1;
p.finish_node();
}
pub(crate) fn multiline_block(p: &mut Parser<'_, '_>) {
p.skip_ws(); p.start_node(MULTILINE_BLOCK);
p.bump(); p.skip_ws();
if p.at(NEWLINE) {
p.bump();
}
skip_blank_lines(p);
if at_sequence_annotation(p) {
sequence_with_annotation(p);
} else if at_multiline_branch_start(p) {
multiline_branches_cond(p);
} else {
conditional_with_expr_standalone(p);
}
p.skip_ws();
skip_blank_lines(p);
if p.current() == R_BRACE {
p.bump();
} else if !p.at_eof() {
p.error("expected `}` to close multiline block".into());
}
p.finish_node();
}
fn inner_logic(p: &mut Parser<'_, '_>, brace_pos: usize) {
if at_sequence_annotation(p) {
sequence_with_annotation(p);
return;
}
if p.current() == NEWLINE {
multiline_conditional(p);
return;
}
let lookahead = p.brace_scan_at(brace_pos);
if lookahead == PIPE {
p.start_node(IMPLICIT_SEQUENCE);
branch_content(p); while p.current() == PIPE {
p.skip_ws();
p.bump(); branch_content(p);
}
p.finish_node();
return;
}
let checkpoint = p.checkpoint();
super::expression::expression(p);
p.skip_ws();
if p.current() == COLON {
p.start_node_at(checkpoint, CONDITIONAL_WITH_EXPR);
p.bump(); conditional_body(p);
p.finish_node();
} else {
p.start_node_at(checkpoint, INNER_EXPRESSION);
p.finish_node();
}
}
fn conditional_body(p: &mut Parser<'_, '_>) {
match p.current() {
NEWLINE => {
p.skip_ws();
if at_multiline_branch_start(p) {
multiline_branches_cond(p);
} else {
branchless_cond_body(p);
}
}
R_BRACE => {
p.skip_ws();
inline_branches_cond(p);
}
_ => {
inline_branches_cond(p);
}
}
}
fn at_sequence_annotation(p: &Parser<'_, '_>) -> bool {
matches!(
p.current(),
AMP | BANG | TILDE | DOLLAR | KW_STOPPING | KW_CYCLE | KW_SHUFFLE | KW_ONCE
)
}
fn sequence_with_annotation(p: &mut Parser<'_, '_>) {
p.start_node(SEQUENCE_WITH_ANNOTATION);
match p.current() {
AMP | BANG | TILDE | DOLLAR => {
p.start_node(SEQUENCE_SYMBOL_ANNOTATION);
while matches!(p.current(), AMP | BANG | TILDE | DOLLAR) {
p.bump();
}
p.finish_node();
}
KW_STOPPING | KW_CYCLE | KW_SHUFFLE | KW_ONCE => {
p.start_node(SEQUENCE_WORD_ANNOTATION);
p.bump(); loop {
p.skip_ws();
if matches!(p.current(), KW_STOPPING | KW_CYCLE | KW_SHUFFLE | KW_ONCE) {
p.bump();
} else {
break;
}
}
p.skip_ws();
p.expect(COLON);
p.finish_node();
}
_ => {
p.error("expected sequence annotation".into());
}
}
p.skip_ws();
if p.current() == R_BRACE {
p.error("expected sequence branches".into());
} else if p.current() == NEWLINE {
multiline_branches_seq(p);
} else {
inline_branches_seq(p);
}
p.finish_node();
}
fn inline_branches_seq(p: &mut Parser<'_, '_>) {
p.start_node(INLINE_BRANCHES_SEQ);
branch_content(p);
while p.current() == PIPE {
p.skip_ws();
p.bump(); branch_content(p);
}
p.finish_node();
}
fn multiline_branches_seq(p: &mut Parser<'_, '_>) {
p.start_node(MULTILINE_BRANCHES_SEQ);
while matches!(p.current(), NEWLINE) {
p.bump();
p.skip_ws();
}
skip_blank_lines(p);
while at_multiline_branch_start(p) {
multiline_branch_seq(p);
}
p.finish_node();
}
fn multiline_branch_seq(p: &mut Parser<'_, '_>) {
p.start_node(MULTILINE_BRANCH_SEQ);
while matches!(p.nth_raw(0), NEWLINE | WHITESPACE) {
p.bump();
}
p.bump(); p.skip_ws();
multiline_branch_body(p);
p.finish_node();
}
fn conditional_with_expr_standalone(p: &mut Parser<'_, '_>) {
p.start_node(CONDITIONAL_WITH_EXPR);
super::expression::expression(p);
p.skip_ws();
if p.current() == COLON {
p.bump();
p.skip_ws();
conditional_body(p);
} else {
p.error("expected `:` in conditional".into());
}
p.finish_node();
}
fn inline_branches_cond(p: &mut Parser<'_, '_>) {
p.start_node(INLINE_BRANCHES_COND);
branch_content(p);
if p.current() == PIPE {
p.skip_ws();
p.bump(); branch_content(p);
}
p.finish_node();
}
fn multiline_branches_cond(p: &mut Parser<'_, '_>) {
p.start_node(MULTILINE_BRANCHES_COND);
while matches!(p.current(), NEWLINE) {
p.bump();
p.skip_ws();
}
skip_blank_lines(p);
while at_multiline_branch_start(p) {
multiline_branch_cond(p);
}
p.finish_node();
}
fn multiline_conditional(p: &mut Parser<'_, '_>) {
p.start_node(MULTILINE_CONDITIONAL);
while matches!(p.current(), NEWLINE) {
p.bump();
p.skip_ws();
}
skip_blank_lines(p);
while at_multiline_branch_start(p) {
multiline_branch_cond(p);
}
p.finish_node();
}
fn multiline_branch_cond(p: &mut Parser<'_, '_>) {
p.start_node(MULTILINE_BRANCH_COND);
while matches!(p.nth_raw(0), NEWLINE | WHITESPACE) {
p.bump();
}
p.bump(); p.skip_ws();
if p.current() == KW_ELSE {
p.bump(); p.skip_ws();
if p.current() == COLON {
p.bump();
}
p.skip_ws();
} else if !matches!(p.current(), NEWLINE | EOF | R_BRACE) && p.current() != MINUS {
let has_condition = looks_like_condition(p);
if has_condition {
super::expression::expression(p);
p.skip_ws();
if p.current() == COLON {
p.bump();
p.skip_ws();
}
}
}
multiline_branch_body(p);
p.finish_node();
}
fn looks_like_condition(p: &Parser<'_, '_>) -> bool {
let mut i = 0;
let mut depth: u32 = 0;
loop {
let kind = p.nth(i);
match kind {
COLON if depth == 0 => return true,
EOF => return false,
NEWLINE | R_BRACE if depth == 0 => return false,
L_BRACE | L_PAREN => depth += 1,
R_BRACE | R_PAREN => depth = depth.saturating_sub(1),
_ => {}
}
i += 1;
}
}
fn branchless_cond_body(p: &mut Parser<'_, '_>) {
p.start_node(BRANCHLESS_COND_BODY);
if p.current() == NEWLINE {
p.bump();
p.skip_ws();
}
let mut at_line_start = true;
loop {
match p.current() {
R_BRACE | EOF => break,
NEWLINE => {
if at_multiline_branch_start(p) {
else_branch(p);
break;
}
p.bump();
p.skip_ws();
at_line_start = true;
}
MINUS if at_line_start => {
else_branch(p);
break;
}
STAR | PLUS => {
super::choice::choice(p);
p.skip_ws();
at_line_start = true;
}
HASH if at_line_start => {
super::tag::tag_line(p);
p.skip_ws();
at_line_start = true;
}
TILDE => {
p.skip_ws();
super::logic::logic_line(p);
p.skip_ws();
at_line_start = true;
}
L_BRACE => {
inline_logic(p);
at_line_start = false;
}
DIVERT | TUNNEL_ONWARDS | THREAD => {
p.skip_ws();
super::divert::divert(p);
at_line_start = false;
}
GLUE => {
p.skip_ws();
p.start_node(GLUE_NODE);
p.bump();
p.finish_node();
at_line_start = false;
}
BACKSLASH => {
if matches!(p.nth(1), NEWLINE | EOF) {
p.start_node(TEXT);
p.bump();
p.finish_node();
} else {
p.start_node(ESCAPE);
p.bump(); p.bump(); p.finish_node();
}
at_line_start = false;
}
_ => {
let before = p.pos();
multiline_branch_text(p);
if p.pos() == before {
break;
}
at_line_start = false;
}
}
}
p.finish_node();
}
fn else_branch(p: &mut Parser<'_, '_>) {
p.start_node(ELSE_BRANCH);
multiline_branch_cond(p);
p.finish_node();
}
fn at_multiline_branch_start(p: &Parser<'_, '_>) -> bool {
let mut i = 0;
loop {
match p.nth(i) {
NEWLINE => {
i += 1;
}
MINUS => return true,
_ => return false,
}
}
}
fn multiline_branch_body(p: &mut Parser<'_, '_>) {
p.start_node(MULTILINE_BRANCH_BODY);
loop {
p.skip_ws();
match p.current() {
EOF | R_BRACE | MINUS => break,
NEWLINE => {
if next_line_is_branch(p) {
break;
}
p.bump();
}
STAR | PLUS => {
super::choice::choice(p);
}
HASH => {
super::tag::tag_line(p);
}
TILDE => {
p.skip_ws();
super::logic::logic_line(p);
}
L_BRACE => {
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(TEXT);
p.bump();
p.finish_node();
} else {
p.start_node(ESCAPE);
p.bump(); p.bump(); p.finish_node();
}
}
DIVERT | TUNNEL_ONWARDS | THREAD => {
super::divert::divert(p);
}
_ => {
let before = p.pos();
multiline_branch_text(p);
if p.pos() == before {
break;
}
}
}
}
p.finish_node();
}
fn next_line_is_branch(p: &Parser<'_, '_>) -> bool {
let mut offset = 1; loop {
match p.nth(offset) {
NEWLINE => offset += 1,
MINUS => return true,
_ => return false,
}
}
}
fn multiline_branch_text(p: &mut Parser<'_, '_>) {
p.start_node(TEXT);
loop {
if p.at_eof() {
break;
}
match p.nth_raw(0) {
NEWLINE | L_BRACE | R_BRACE | GLUE | DIVERT | TUNNEL_ONWARDS | LINE_COMMENT
| BLOCK_COMMENT | BACKSLASH | EOF | TILDE | THREAD => break,
_ => p.bump(),
}
}
p.finish_node();
}
fn branch_content(p: &mut Parser<'_, '_>) {
p.start_node(BRANCH_CONTENT);
loop {
match p.current() {
PIPE | R_BRACE | NEWLINE | EOF => break,
HASH => {
branch_tags(p);
break;
}
L_BRACE => {
p.skip_ws();
inline_logic(p);
}
GLUE => {
p.skip_ws();
p.start_node(GLUE_NODE);
p.bump();
p.finish_node();
}
DIVERT | TUNNEL_ONWARDS | THREAD => {
p.skip_ws();
super::divert::divert(p);
}
BACKSLASH => {
if matches!(p.nth(1), NEWLINE | EOF) {
p.start_node(TEXT);
p.bump();
p.finish_node();
} else {
p.start_node(ESCAPE);
p.bump(); p.bump(); p.finish_node();
}
}
_ => {
let before = p.pos();
branch_text(p);
if p.pos() == before {
break;
}
}
}
}
p.finish_node();
}
fn branch_tags(p: &mut Parser<'_, '_>) {
p.start_node(TAGS);
while p.current() == HASH {
branch_tag(p);
}
p.finish_node();
}
fn branch_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 | PIPE | R_BRACE => break,
_ => p.bump(),
}
}
p.finish_node();
}
fn branch_text(p: &mut Parser<'_, '_>) {
p.start_node(TEXT);
loop {
if p.at_eof() {
break;
}
match p.nth_raw(0) {
PIPE | L_BRACE | R_BRACE | GLUE | DIVERT | TUNNEL_ONWARDS | LINE_COMMENT
| BLOCK_COMMENT | NEWLINE | BACKSLASH | EOF | THREAD | HASH => break,
_ => p.bump(),
}
}
p.finish_node();
}
fn skip_blank_lines(p: &mut Parser<'_, '_>) {
while p.current() == NEWLINE {
p.bump();
p.skip_ws();
}
}