use rustc_hash::FxHashSet;
use crate::{
green::{GreenElement, GreenNode, GreenToken, SyntaxKind},
NodeOrToken, SmolStr,
};
#[derive(Default, Debug)]
pub struct NodeCache {
nodes: FxHashSet<GreenNode>,
tokens: FxHashSet<GreenToken>,
}
impl NodeCache {
fn node<I>(&mut self, kind: SyntaxKind, children: I) -> GreenNode
where
I: IntoIterator<Item = GreenElement>,
I::IntoIter: ExactSizeIterator,
{
let mut node = GreenNode::new(kind, children);
if node.children().len() <= 3 {
match self.nodes.get(&node) {
Some(existing) => node = existing.clone(),
None => assert!(self.nodes.insert(node.clone())),
}
}
node
}
fn token(&mut self, kind: SyntaxKind, text: SmolStr) -> GreenToken {
let mut token = GreenToken::new(kind, text);
match self.tokens.get(&token) {
Some(existing) => token = existing.clone(),
None => assert!(self.tokens.insert(token.clone())),
}
token
}
}
#[derive(Debug)]
enum MaybeOwned<'a, T> {
Owned(T),
Borrowed(&'a mut T),
}
impl<T> std::ops::Deref for MaybeOwned<'_, T> {
type Target = T;
fn deref(&self) -> &T {
match self {
MaybeOwned::Owned(it) => it,
MaybeOwned::Borrowed(it) => *it,
}
}
}
impl<T> std::ops::DerefMut for MaybeOwned<'_, T> {
fn deref_mut(&mut self) -> &mut T {
match self {
MaybeOwned::Owned(it) => it,
MaybeOwned::Borrowed(it) => *it,
}
}
}
impl<T: Default> Default for MaybeOwned<'_, T> {
fn default() -> Self {
MaybeOwned::Owned(T::default())
}
}
#[derive(Clone, Copy, Debug)]
pub struct Checkpoint(usize);
#[derive(Default, Debug)]
pub struct GreenNodeBuilder<'cache> {
cache: MaybeOwned<'cache, NodeCache>,
parents: Vec<(SyntaxKind, usize)>,
children: Vec<GreenElement>,
}
impl GreenNodeBuilder<'_> {
pub fn new() -> GreenNodeBuilder<'static> {
GreenNodeBuilder::default()
}
pub fn with_cache(cache: &mut NodeCache) -> GreenNodeBuilder<'_> {
GreenNodeBuilder {
cache: MaybeOwned::Borrowed(cache),
parents: Vec::new(),
children: Vec::new(),
}
}
#[inline]
pub fn token(&mut self, kind: SyntaxKind, text: SmolStr) {
let token = self.cache.token(kind, text);
self.children.push(token.into());
}
#[inline]
pub fn start_node(&mut self, kind: SyntaxKind) {
let len = self.children.len();
self.parents.push((kind, len));
}
#[inline]
pub fn finish_node(&mut self) {
let (kind, first_child) = self.parents.pop().unwrap();
let children = self.children.drain(first_child..);
let node = self.cache.node(kind, children);
self.children.push(node.into());
}
#[inline]
pub fn checkpoint(&self) -> Checkpoint {
Checkpoint(self.children.len())
}
#[inline]
pub fn start_node_at(&mut self, checkpoint: Checkpoint, kind: SyntaxKind) {
let Checkpoint(checkpoint) = checkpoint;
assert!(
checkpoint <= self.children.len(),
"checkpoint no longer valid, was finish_node called early?"
);
if let Some(&(_, first_child)) = self.parents.last() {
assert!(
checkpoint >= first_child,
"checkpoint no longer valid, was an unmatched start_node_at called?"
);
}
self.parents.push((kind, checkpoint));
}
#[inline]
pub fn finish(mut self) -> GreenNode {
assert_eq!(self.children.len(), 1);
match self.children.pop().unwrap() {
NodeOrToken::Node(node) => node,
NodeOrToken::Token(_) => panic!(),
}
}
}