use crate::span::{NodeId, SourceId, Span, TokenId, TriviaId, NONE_U32};
use crate::syntax_kind::SyntaxKind;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u16)]
pub enum CstEdgeKind {
Node = 0,
Token = 1,
}
#[derive(Debug, Default, Clone, Copy)]
#[repr(C)]
pub(crate) struct CstNodeRecord {
pub kind: u16,
pub flags: u16,
pub span_start: u32,
pub span_end: u32,
pub first_child: u32,
pub child_count: u32,
pub data_ref: u32,
}
#[derive(Debug, Default, Clone, Copy)]
#[repr(C)]
pub(crate) struct CstEdgeRecord {
pub kind: u16,
pub flags: u16,
pub ref_id: u32,
}
#[derive(Debug, Default, Clone, Copy)]
#[repr(C)]
pub(crate) struct TokenRecord {
pub kind: u16,
pub flags: u16,
pub source_id: u32,
pub span_start: u32,
pub span_end: u32,
pub first_trivia: u32,
pub leading_trivia_count: u16,
pub trailing_trivia_count: u16,
}
#[derive(Debug, Default, Clone, Copy)]
#[repr(C)]
pub(crate) struct TriviaRecord {
pub kind: u16,
pub flags: u16,
pub source_id: u32,
pub span_start: u32,
pub span_end: u32,
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct PendingNode {
pub kind: SyntaxKind,
pub flags: u16,
pub span_start: u32,
pub frame_depth: u32,
pub edge_start: u32,
}
#[derive(Debug, Default, Clone)]
pub struct CstTables {
pub(crate) nodes: Vec<CstNodeRecord>,
pub(crate) edges: Vec<CstEdgeRecord>,
pub(crate) tokens: Vec<TokenRecord>,
pub(crate) trivia: Vec<TriviaRecord>,
}
impl CstTables {
pub fn new() -> Self {
Self::default()
}
#[inline]
pub fn node_count(&self) -> usize {
self.nodes.len()
}
#[inline]
pub fn token_count(&self) -> usize {
self.tokens.len()
}
#[inline]
pub fn trivia_count(&self) -> usize {
self.trivia.len()
}
#[inline]
pub fn edge_count(&self) -> usize {
self.edges.len()
}
#[inline]
pub fn root_id(&self) -> Option<NodeId> {
if self.nodes.is_empty() {
None
} else {
Some(NodeId::new((self.nodes.len() - 1) as u32))
}
}
pub fn clear(&mut self) {
self.nodes.clear();
self.edges.clear();
self.tokens.clear();
self.trivia.clear();
}
pub fn reserve(&mut self, capacity: &CstCapacity) {
self.nodes.reserve(capacity.nodes);
self.edges.reserve(capacity.edges);
self.tokens.reserve(capacity.tokens);
self.trivia.reserve(capacity.trivia);
}
pub fn shrink_to_fit(&mut self) {
self.nodes.shrink_to_fit();
self.edges.shrink_to_fit();
self.tokens.shrink_to_fit();
self.trivia.shrink_to_fit();
}
#[inline]
pub(crate) fn node_at(&self, id: NodeId) -> &CstNodeRecord {
&self.nodes[id.index()]
}
#[inline]
pub(crate) fn token_at(&self, id: TokenId) -> &TokenRecord {
&self.tokens[id.index()]
}
#[inline]
pub(crate) fn trivia_at(&self, id: TriviaId) -> &TriviaRecord {
&self.trivia[id.index()]
}
#[inline]
pub(crate) fn edges_for(&self, node: &CstNodeRecord) -> &[CstEdgeRecord] {
let start = node.first_child as usize;
let end = start + node.child_count as usize;
&self.edges[start..end]
}
}
#[derive(Debug, Default, Clone, Copy)]
pub struct CstCapacity {
pub nodes: usize,
pub edges: usize,
pub tokens: usize,
pub trivia: usize,
}
#[derive(Debug, Default)]
pub(crate) struct CstBuilder {
pub(crate) tables: CstTables,
pub(crate) pending_edges: Vec<CstEdgeRecord>,
pub(crate) frame_starts: Vec<u32>,
}
#[derive(Debug, Default, Clone, Copy)]
#[allow(dead_code)] pub(crate) struct BuilderLengths {
pub nodes: u32,
pub edges: u32,
pub tokens: u32,
pub trivia: u32,
pub frame_depth: u32,
pub pending_edges: u32,
}
impl CstBuilder {
pub fn new() -> Self {
Self::default()
}
#[allow(dead_code)]
pub fn lengths(&self) -> BuilderLengths {
BuilderLengths {
nodes: self.tables.nodes.len() as u32,
edges: self.tables.edges.len() as u32,
tokens: self.tables.tokens.len() as u32,
trivia: self.tables.trivia.len() as u32,
frame_depth: self.frame_starts.len() as u32,
pending_edges: self.pending_edges.len() as u32,
}
}
#[allow(dead_code)]
pub fn rollback_to(&mut self, lengths: BuilderLengths) {
self.tables.nodes.truncate(lengths.nodes as usize);
self.tables.edges.truncate(lengths.edges as usize);
self.tables.tokens.truncate(lengths.tokens as usize);
self.tables.trivia.truncate(lengths.trivia as usize);
self.frame_starts.truncate(lengths.frame_depth as usize);
self.pending_edges.truncate(lengths.pending_edges as usize);
}
pub fn start_node(&mut self, kind: SyntaxKind, span_start: u32) -> PendingNode {
let edge_start = self.pending_edges.len() as u32;
let depth = self.frame_starts.len() as u32;
self.frame_starts.push(edge_start);
PendingNode {
kind,
flags: 0,
span_start,
frame_depth: depth,
edge_start,
}
}
pub fn finish_node(&mut self, pending: PendingNode, span_end: u32) -> NodeId {
debug_assert_eq!(
pending.frame_depth as usize,
self.frame_starts.len() - 1,
"finish_node out of nesting order"
);
let edge_start = self.frame_starts.pop().expect("frame for pending node");
debug_assert_eq!(edge_start, pending.edge_start);
let first_child = self.tables.edges.len() as u32;
let edge_start_us = edge_start as usize;
let child_count = (self.pending_edges.len() - edge_start_us) as u32;
self.tables
.edges
.extend_from_slice(&self.pending_edges[edge_start_us..]);
self.pending_edges.truncate(edge_start_us);
let id = NodeId::new(self.tables.nodes.len() as u32);
self.tables.nodes.push(CstNodeRecord {
kind: pending.kind.as_u16(),
flags: pending.flags,
span_start: pending.span_start,
span_end,
first_child,
child_count,
data_ref: NONE_U32,
});
id
}
pub fn push_node_edge(&mut self, child: NodeId) {
debug_assert!(
!self.frame_starts.is_empty(),
"a node must be started before pushing edges"
);
self.pending_edges.push(CstEdgeRecord {
kind: CstEdgeKind::Node as u16,
flags: 0,
ref_id: child.raw(),
});
}
pub fn push_token_edge(&mut self, token: TokenId) {
debug_assert!(
!self.frame_starts.is_empty(),
"a node must be started before pushing edges"
);
self.pending_edges.push(CstEdgeRecord {
kind: CstEdgeKind::Token as u16,
flags: 0,
ref_id: token.raw(),
});
}
pub fn push_token(
&mut self,
kind: SyntaxKind,
source: SourceId,
span: Span,
first_trivia: u32,
leading_trivia_count: u16,
trailing_trivia_count: u16,
) -> TokenId {
let id = TokenId::new(self.tables.tokens.len() as u32);
self.tables.tokens.push(TokenRecord {
kind: kind.as_u16(),
flags: 0,
source_id: source.raw(),
span_start: span.start,
span_end: span.end,
first_trivia,
leading_trivia_count,
trailing_trivia_count,
});
id
}
pub fn push_trivia(&mut self, kind: SyntaxKind, source: SourceId, span: Span) -> TriviaId {
let id = TriviaId::new(self.tables.trivia.len() as u32);
self.tables.trivia.push(TriviaRecord {
kind: kind.as_u16(),
flags: 0,
source_id: source.raw(),
span_start: span.start,
span_end: span.end,
});
id
}
}
#[cfg(test)]
mod tests {
use super::*;
use core::mem::size_of;
#[test]
fn record_sizes_stay_within_budget() {
assert_eq!(size_of::<CstNodeRecord>(), 24);
assert_eq!(size_of::<CstEdgeRecord>(), 8);
assert_eq!(size_of::<TokenRecord>(), 24);
assert_eq!(size_of::<TriviaRecord>(), 16);
}
#[test]
fn builder_finishes_node_with_child_range() {
let mut b = CstBuilder::new();
let pending = b.start_node(SyntaxKind::Pattern, 0);
let token = b.push_token(
SyntaxKind::TextToken,
SourceId::new(0),
Span::new(0, 5),
0,
0,
0,
);
b.push_token_edge(token);
let node_id = b.finish_node(pending, 5);
let node = b.tables.node_at(node_id);
assert_eq!(node.kind, SyntaxKind::Pattern.as_u16());
assert_eq!(node.span_start, 0);
assert_eq!(node.span_end, 5);
assert_eq!(node.first_child, 0);
assert_eq!(node.child_count, 1);
assert_eq!(node.data_ref, NONE_U32);
assert_eq!(b.tables.token_count(), 1);
assert_eq!(b.tables.edge_count(), 1);
}
#[test]
fn rollback_truncates_table_lengths() {
let mut b = CstBuilder::new();
let mark = b.lengths();
let pending = b.start_node(SyntaxKind::Pattern, 0);
let _ = b.push_token(
SyntaxKind::TextToken,
SourceId::new(0),
Span::new(0, 3),
0,
0,
0,
);
let _ = b.finish_node(pending, 3);
assert_eq!(b.tables.node_count(), 1);
assert_eq!(b.tables.token_count(), 1);
b.rollback_to(mark);
assert_eq!(b.tables.node_count(), 0);
assert_eq!(b.tables.token_count(), 0);
assert_eq!(b.tables.edge_count(), 0);
}
}