use brink_syntax::SyntaxKind;
use brink_syntax::ast::{self, AstNode};
use crate::Block;
use super::content::{ContentAccumulator, DirectBackend};
use super::context::{LowerScope, LowerSink};
pub enum BodyChild {
ContentLine(ast::ContentLine),
LogicLine(ast::LogicLine),
TagLine(ast::TagLine),
DivertNode(ast::DivertNode),
InlineLogic(ast::InlineLogic),
MultilineBlock(ast::MultilineBlock),
Choice(ast::Choice),
Gather(ast::Gather),
Structural,
Trivia,
}
pub fn classify_body_child(node: &brink_syntax::SyntaxNode) -> BodyChild {
match node.kind() {
SyntaxKind::CONTENT_LINE => {
ast::ContentLine::cast(node.clone()).map_or(BodyChild::Trivia, BodyChild::ContentLine)
}
SyntaxKind::LOGIC_LINE => {
ast::LogicLine::cast(node.clone()).map_or(BodyChild::Trivia, BodyChild::LogicLine)
}
SyntaxKind::TAG_LINE => {
ast::TagLine::cast(node.clone()).map_or(BodyChild::Trivia, BodyChild::TagLine)
}
SyntaxKind::DIVERT_NODE => {
ast::DivertNode::cast(node.clone()).map_or(BodyChild::Trivia, BodyChild::DivertNode)
}
SyntaxKind::INLINE_LOGIC => {
ast::InlineLogic::cast(node.clone()).map_or(BodyChild::Trivia, BodyChild::InlineLogic)
}
SyntaxKind::MULTILINE_BLOCK => ast::MultilineBlock::cast(node.clone())
.map_or(BodyChild::Trivia, BodyChild::MultilineBlock),
SyntaxKind::CHOICE => {
ast::Choice::cast(node.clone()).map_or(BodyChild::Trivia, BodyChild::Choice)
}
SyntaxKind::GATHER => {
ast::Gather::cast(node.clone()).map_or(BodyChild::Trivia, BodyChild::Gather)
}
SyntaxKind::KNOT_DEF
| SyntaxKind::KNOT_HEADER
| SyntaxKind::STITCH_DEF
| SyntaxKind::STITCH_HEADER
| SyntaxKind::VAR_DECL
| SyntaxKind::CONST_DECL
| SyntaxKind::LIST_DECL
| SyntaxKind::EXTERNAL_DECL
| SyntaxKind::INCLUDE_STMT
| SyntaxKind::STRAY_CLOSING_BRACE
| SyntaxKind::AUTHOR_WARNING
| SyntaxKind::CHOICE_BULLETS
| SyntaxKind::LABEL
| SyntaxKind::CHOICE_CONDITION
| SyntaxKind::CHOICE_START_CONTENT
| SyntaxKind::CHOICE_BRACKET_CONTENT
| SyntaxKind::CHOICE_INNER_CONTENT
| SyntaxKind::TAGS
| SyntaxKind::MIXED_CONTENT
| SyntaxKind::GATHER_DASHES => BodyChild::Structural,
SyntaxKind::EMPTY_LINE => BodyChild::Trivia,
other => {
debug_assert!(
other.is_trivia(),
"unexpected SyntaxKind in classify_body_child: {other:?}"
);
BodyChild::Trivia
}
}
}
pub enum BranchChild {
ContentLine(ast::ContentLine),
LogicLine(ast::LogicLine),
TagLine(ast::TagLine),
DivertNode(ast::DivertNode),
InlineLogic(ast::InlineLogic),
Choice(ast::Choice),
Text(String),
Glue,
Escape(String),
Newline,
Whitespace(String),
Stop,
Trivia,
}
pub fn classify_branch_child(
child: &rowan::NodeOrToken<brink_syntax::SyntaxNode, brink_syntax::SyntaxToken>,
) -> BranchChild {
match child {
rowan::NodeOrToken::Token(token) => match token.kind() {
SyntaxKind::NEWLINE => BranchChild::Newline,
SyntaxKind::WHITESPACE => BranchChild::Whitespace(token.text().to_string()),
_ => BranchChild::Trivia,
},
rowan::NodeOrToken::Node(node) => match node.kind() {
SyntaxKind::CONTENT_LINE => ast::ContentLine::cast(node.clone())
.map_or(BranchChild::Trivia, BranchChild::ContentLine),
SyntaxKind::LOGIC_LINE => ast::LogicLine::cast(node.clone())
.map_or(BranchChild::Trivia, BranchChild::LogicLine),
SyntaxKind::TAG_LINE => {
ast::TagLine::cast(node.clone()).map_or(BranchChild::Trivia, BranchChild::TagLine)
}
SyntaxKind::DIVERT_NODE => ast::DivertNode::cast(node.clone())
.map_or(BranchChild::Trivia, BranchChild::DivertNode),
SyntaxKind::INLINE_LOGIC => ast::InlineLogic::cast(node.clone())
.map_or(BranchChild::Trivia, BranchChild::InlineLogic),
SyntaxKind::CHOICE => {
ast::Choice::cast(node.clone()).map_or(BranchChild::Trivia, BranchChild::Choice)
}
SyntaxKind::ELSE_BRANCH => BranchChild::Stop,
SyntaxKind::TEXT => {
let text = node.text().to_string();
if text.is_empty() {
BranchChild::Trivia
} else {
BranchChild::Text(text)
}
}
SyntaxKind::GLUE_NODE => BranchChild::Glue,
SyntaxKind::ESCAPE => {
let text = node.text().to_string();
if text.len() > 1 {
BranchChild::Escape(text)
} else {
BranchChild::Trivia
}
}
other if other.is_token() => BranchChild::Trivia,
other => {
debug_assert!(
other.is_trivia(),
"unexpected SyntaxKind in classify_branch_child: {other:?}"
);
BranchChild::Trivia
}
},
}
}
pub fn lower_simple_body(
parent: &brink_syntax::SyntaxNode,
scope: &LowerScope,
sink: &mut impl LowerSink,
) -> Block {
let mut acc = ContentAccumulator::new(DirectBackend::new());
for child in parent.children() {
match classify_body_child(&child) {
BodyChild::ContentLine(cl) => {
acc.handle(&cl, scope, sink);
}
BodyChild::LogicLine(ll) => {
acc.handle(&ll, scope, sink);
}
BodyChild::TagLine(tl) => {
acc.handle(&tl, scope, sink);
}
BodyChild::DivertNode(dn) => {
acc.handle(&dn, scope, sink);
}
BodyChild::InlineLogic(il) => {
acc.handle(&il, scope, sink);
}
BodyChild::MultilineBlock(mb) => {
acc.handle(&mb, scope, sink);
}
BodyChild::Choice(_)
| BodyChild::Gather(_)
| BodyChild::Structural
| BodyChild::Trivia => {}
}
}
acc.finish()
}