use crate::SyntaxKind::{
ARG_LIST, COMMA, DIVERT, DIVERT_NODE, DIVERT_TARGET_WITH_ARGS, DOT, IDENT, KW_DONE, KW_END,
L_PAREN, PATH, R_PAREN, SIMPLE_DIVERT, THREAD, THREAD_START, TUNNEL_CALL_NODE, TUNNEL_ONWARDS,
TUNNEL_ONWARDS_NODE,
};
use super::Parser;
pub(crate) fn at_divert(p: &Parser<'_, '_>) -> bool {
matches!(p.current(), DIVERT | TUNNEL_ONWARDS | THREAD)
}
pub(crate) fn divert(p: &mut Parser<'_, '_>) {
p.start_node(DIVERT_NODE);
match p.current() {
THREAD => thread_start(p),
TUNNEL_ONWARDS => tunnel_onwards(p),
DIVERT => divert_chain(p),
_ => {
p.error("expected divert".into());
}
}
p.finish_node();
}
fn thread_start(p: &mut Parser<'_, '_>) {
p.start_node(THREAD_START);
p.bump(); p.skip_ws();
path(p);
p.skip_ws();
if p.current() == L_PAREN {
p.bump(); p.skip_ws();
if p.current() != R_PAREN {
arg_list(p);
}
p.skip_ws();
p.expect(R_PAREN);
}
p.finish_node();
}
fn tunnel_onwards(p: &mut Parser<'_, '_>) {
p.start_node(TUNNEL_ONWARDS_NODE);
p.bump(); p.skip_ws();
if p.current() == DIVERT {
divert_chain(p);
} else if at_divert_target(p) {
divert_target_with_args(p);
}
p.finish_node();
}
fn divert_chain(p: &mut Parser<'_, '_>) {
let checkpoint = p.checkpoint();
p.bump(); p.skip_ws();
let mut has_target = false;
let mut trailing_arrow = false;
if at_divert_target(p) {
divert_target_with_args(p);
has_target = true;
trailing_arrow = false;
}
loop {
p.skip_ws();
if p.current() != DIVERT {
break;
}
p.bump(); trailing_arrow = true;
p.skip_ws();
if at_divert_target(p) {
divert_target_with_args(p);
has_target = true;
trailing_arrow = false;
}
}
if has_target && (trailing_arrow || p.current() == TUNNEL_ONWARDS) {
p.start_node_at(checkpoint, TUNNEL_CALL_NODE);
p.finish_node();
} else {
p.start_node_at(checkpoint, SIMPLE_DIVERT);
p.finish_node();
}
}
fn at_divert_target(p: &Parser<'_, '_>) -> bool {
p.current() == KW_DONE || p.current() == KW_END || p.at_ident_or_keyword()
}
fn divert_target_with_args(p: &mut Parser<'_, '_>) {
p.start_node(DIVERT_TARGET_WITH_ARGS);
match p.current() {
KW_DONE | KW_END => {
p.bump();
}
_ if p.at_ident_or_keyword() => {
path(p);
}
_ => {
p.error("expected divert target".into());
}
}
p.skip_ws();
if p.current() == L_PAREN {
p.bump(); p.skip_ws();
if p.current() != R_PAREN {
arg_list(p);
}
p.skip_ws();
p.expect(R_PAREN);
}
p.finish_node();
}
pub(crate) fn path(p: &mut Parser<'_, '_>) {
p.start_node(PATH);
p.expect_ident_or_keyword();
while p.current() == DOT && (p.nth(1) == IDENT || p.nth(1).is_keyword()) {
p.bump(); p.bump(); }
p.finish_node();
}
pub(crate) fn arg_list(p: &mut Parser<'_, '_>) {
p.start_node(ARG_LIST);
super::expression::expression(p);
loop {
p.skip_ws();
if !p.eat(COMMA) {
break;
}
p.skip_ws();
super::expression::expression(p);
}
p.finish_node();
}