use rowan::{GreenNode, GreenNodeBuilder};
use crate::{
SyntaxKind,
lexer::LexToken,
parser::{Event, SyntaxError},
};
#[derive(Debug, Default)]
pub(crate) struct SyntaxTreeBuilder {
inner: GreenNodeBuilder<'static>,
errors: Vec<SyntaxError>,
}
impl SyntaxTreeBuilder {
pub(crate) fn build(
tokens: &[LexToken],
mut events: Vec<Event>,
) -> (GreenNode, Vec<SyntaxError>) {
let mut builder = SyntaxTreeBuilder::default();
let mut tok_idx: usize = 0;
let mut forward_parents: Vec<SyntaxKind> = Vec::new();
let last_finish_idx = events
.iter()
.rposition(|e| matches!(e, Event::Finish))
.unwrap_or(usize::MAX);
for i in 0..events.len() {
match std::mem::replace(&mut events[i], Event::tombstone()) {
Event::Start {
kind,
forward_parent,
} => {
forward_parents.clear();
forward_parents.push(kind);
let mut idx = i;
let mut fp = forward_parent;
while let Some(rel) = fp {
idx += rel as usize;
match std::mem::replace(&mut events[idx], Event::tombstone()) {
Event::Start {
kind: outer_kind,
forward_parent: next_fp,
} => {
forward_parents.push(outer_kind);
fp = next_fp;
}
_ => unreachable!("forward parent at event {idx} must be a Start node"),
}
}
for &k in forward_parents.iter().rev() {
builder.inner.start_node(rowan::SyntaxKind(k as u16));
}
}
Event::Finish => {
if i == last_finish_idx {
while tok_idx < tokens.len() {
builder.inner.token(
rowan::SyntaxKind(tokens[tok_idx].kind as u16),
&tokens[tok_idx].text,
);
tok_idx += 1;
}
}
builder.inner.finish_node();
}
Event::Token { kind } => {
while tok_idx < tokens.len() && tokens[tok_idx].kind.is_trivia() {
builder.inner.token(
rowan::SyntaxKind(tokens[tok_idx].kind as u16),
&tokens[tok_idx].text,
);
tok_idx += 1;
}
debug_assert!(
tok_idx < tokens.len(),
"Token event past end of token stream (kind={kind:?})"
);
debug_assert_eq!(
tokens[tok_idx].kind, kind,
"Token event kind mismatch: expected {:?}, saw {:?}",
tokens[tok_idx].kind, kind
);
builder.inner.token(
rowan::SyntaxKind(tokens[tok_idx].kind as u16),
&tokens[tok_idx].text,
);
tok_idx += 1;
}
Event::Error { code, msg, offset } => {
builder.errors.push(SyntaxError {
code,
message: msg,
offset,
});
}
Event::Abandoned => {}
}
}
debug_assert!(
tok_idx == tokens.len(),
"{} trivia token(s) unflushed after build",
tokens.len() - tok_idx
);
(builder.inner.finish(), builder.errors)
}
}