use alloc::vec::Vec;
use core::fmt;
use span_lang::Span;
use token_lang::Token;
use crate::tree::{Element, Node};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum BuildError {
EmptyTree,
UnclosedNodes,
UnbalancedFinish,
TokenOutsideNode,
MultipleRoots,
}
impl fmt::Display for BuildError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let message = match self {
BuildError::EmptyTree => "no node was started before finishing the tree",
BuildError::UnclosedNodes => "the tree was finished with nodes still open",
BuildError::UnbalancedFinish => "a node was closed with no open node to close",
BuildError::TokenOutsideNode => "a token was pushed before any node was started",
BuildError::MultipleRoots => "a second root node was started after the first closed",
};
f.write_str(message)
}
}
impl core::error::Error for BuildError {}
struct Partial<K> {
kind: K,
children: Vec<Element<K>>,
}
pub struct Builder<K> {
stack: Vec<Partial<K>>,
root: Option<Node<K>>,
cursor: u32,
error: Option<BuildError>,
}
impl<K> Builder<K> {
#[inline]
#[must_use]
pub const fn new() -> Self {
Self {
stack: Vec::new(),
root: None,
cursor: 0,
error: None,
}
}
#[inline]
#[must_use]
pub fn is_empty(&self) -> bool {
self.stack.is_empty() && self.root.is_none()
}
pub fn start_node(&mut self, kind: K) {
if self.error.is_some() {
return;
}
if self.root.is_some() && self.stack.is_empty() {
self.fail(BuildError::MultipleRoots);
return;
}
self.stack.push(Partial {
kind,
children: Vec::new(),
});
}
pub fn token(&mut self, token: Token<K>) {
if self.error.is_some() {
return;
}
match self.stack.last_mut() {
Some(top) => {
self.cursor = self.cursor.max(token.span().end().to_u32());
top.children.push(Element::Token(token));
}
None => self.fail(BuildError::TokenOutsideNode),
}
}
pub fn finish_node(&mut self) {
if self.error.is_some() {
return;
}
let Some(partial) = self.stack.pop() else {
self.fail(BuildError::UnbalancedFinish);
return;
};
let node = self.build(partial);
match self.stack.last_mut() {
Some(parent) => parent.children.push(Element::Node(node)),
None => {
if self.root.is_some() {
self.fail(BuildError::MultipleRoots);
} else {
self.root = Some(node);
}
}
}
}
pub fn finish(self) -> Result<Node<K>, BuildError> {
if let Some(error) = self.error {
return Err(error);
}
if !self.stack.is_empty() {
return Err(BuildError::UnclosedNodes);
}
self.root.ok_or(BuildError::EmptyTree)
}
fn build(&self, partial: Partial<K>) -> Node<K> {
if partial.children.is_empty() {
Node::with_span(partial.kind, partial.children, Span::empty(self.cursor))
} else {
Node::new(partial.kind, partial.children)
}
}
fn fail(&mut self, error: BuildError) {
if self.error.is_none() {
self.error = Some(error);
}
}
}
impl<K> Default for Builder<K> {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl<K: fmt::Debug> fmt::Debug for Builder<K> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Builder")
.field("open_nodes", &self.stack.len())
.field("has_root", &self.root.is_some())
.field("error", &self.error)
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec::Vec;
fn t(kind: &'static str, lo: u32, hi: u32) -> Token<&'static str> {
Token::new(kind, Span::new(lo, hi))
}
#[test]
fn test_builds_flat_tree() {
let mut b = Builder::new();
b.start_node("root");
b.token(t("a", 0, 1));
b.token(t("b", 1, 2));
b.finish_node();
let root = b.finish().expect("balanced");
assert_eq!(root.kind(), &"root");
assert_eq!(root.span(), Span::new(0, 2));
assert_eq!(root.child_tokens().count(), 2);
}
#[test]
fn test_builds_nested_tree_with_covering_spans() {
let mut b = Builder::new();
b.start_node("outer");
b.token(t("(", 0, 1));
b.start_node("inner");
b.token(t("x", 1, 2));
b.finish_node();
b.token(t(")", 2, 3));
b.finish_node();
let root = b.finish().expect("balanced");
assert_eq!(root.span(), Span::new(0, 3));
let inner = root.child_nodes().next().expect("one inner node");
assert_eq!(inner.span(), Span::new(1, 2));
let leaves: Vec<_> = root.tokens().map(|tok| *tok.kind()).collect();
assert_eq!(leaves, ["(", "x", ")"]);
}
#[test]
fn test_empty_node_takes_cursor_position() {
let mut b = Builder::new();
b.start_node("root");
b.token(t("a", 0, 3));
b.start_node("gap"); b.finish_node();
b.finish_node();
let root = b.finish().expect("balanced");
let gap = root.child_nodes().next().expect("gap node");
assert_eq!(gap.span(), Span::empty(3));
assert!(gap.is_empty());
}
#[test]
fn test_finish_empty_is_error() {
let b = Builder::<&str>::new();
assert_eq!(b.finish(), Err(BuildError::EmptyTree));
}
#[test]
fn test_unclosed_node_is_error() {
let mut b = Builder::new();
b.start_node("root");
b.token(t("a", 0, 1));
assert_eq!(b.finish(), Err(BuildError::UnclosedNodes));
}
#[test]
fn test_unbalanced_finish_is_error() {
let mut b = Builder::<&str>::new();
b.finish_node();
assert_eq!(b.finish(), Err(BuildError::UnbalancedFinish));
}
#[test]
fn test_token_outside_node_is_error() {
let mut b = Builder::new();
b.token(t("a", 0, 1));
assert_eq!(b.finish(), Err(BuildError::TokenOutsideNode));
}
#[test]
fn test_multiple_roots_is_error() {
let mut b = Builder::new();
b.start_node("first");
b.token(t("a", 0, 1));
b.finish_node();
b.start_node("second");
b.token(t("b", 1, 2));
b.finish_node();
assert_eq!(b.finish(), Err(BuildError::MultipleRoots));
}
#[test]
fn test_first_error_wins() {
let mut b = Builder::<&str>::new();
b.finish_node(); b.token(t("a", 0, 1)); assert_eq!(b.finish(), Err(BuildError::UnbalancedFinish));
}
#[test]
fn test_deeply_nested_build_and_finish() {
let mut b = Builder::new();
for _ in 0..10_000 {
b.start_node("link");
}
b.token(t("leaf", 0, 1));
for _ in 0..10_000 {
b.finish_node();
}
let root = b.finish().expect("balanced");
assert_eq!(root.descendants().count(), 10_000);
assert_eq!(root.tokens().count(), 1);
}
}