#[allow(clippy::crate_in_macro_def)]
#[macro_export]
macro_rules! define_node_db {
(
$db_name:ident =>
$(
crate::$($node_name:ident)::+
),* $(,)?
) => {
paste::paste! {
#[derive(Clone, PartialEq)]
pub enum Node {
$(
[< $($node_name:camel)+ >] {
span: laburnum::Span,
leading: Option<[< $db_name NodeId >]>,
node: crate:: $($node_name)::+,
trailing: Option<[< $db_name NodeId >]>,
},
)*
}
impl std::fmt::Display for Node {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
$(
| Node :: [< $($node_name:camel)+ >] {
..
} => {
write!(f,
"{}",
stringify!([< $($node_name:camel)+ >]),
)
},
)*
}
}
}
impl std::fmt::Debug for Node {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
$(
| Node :: [< $($node_name:camel)+ >] {
leading,
node,
trailing,
..
} => {
f.debug_struct(
stringify!([< $($node_name:camel)+ >])
)
.field("leading", leading)
.field("node", node)
.field("trailing", trailing)
.finish()
},
)*
}
}
}
impl bluegum::Bluegum for Node {
fn node(&self, b: &mut bluegum::Builder) {
b.name("Node")
.field("id", format!("{:?}",&self))
.debug("Help", "For the full node use `node_with_state`");
}
}
impl<'state> bluegum::BluegumWithState<Printer<'state>> for Node {
#[allow(unused)]
fn node_with_state(
&self,
b: &mut bluegum::Builder,
state: &Printer<'state>,
) {
match self {
$(
| Node::[< $($node_name:camel)+ >] {
leading,
node,
trailing,
..
} => {
if let Some(l) = leading {
b.add_node_with_state(state, "leading_meta", l);
}
node.node_with_state(b,state);
if let Some(t) = trailing {
b.add_node_with_state(state, "trailing_meta", t);
}
},
)*
}
}
}
#[allow(unused)]
impl Node {
#[allow(unused)]
$(
pub fn [< match_ $($node_name:snake)_+ >] (
&self,
) -> Option<&crate::$($node_name)::+>
{
match self {
| Node::[< $($node_name:camel)+ >] {
node,
..
} => Some(node),
_ => None,
}
}
)*
pub fn span(&self) -> laburnum::Span {
match &self {
$(
| Node :: [< $($node_name:camel)+ >]
{
span,
..
} => *span,
)*
}
}
pub fn leading(&self) -> Option<[< $db_name NodeId >]> {
match &self {
$(
| Node :: [< $($node_name:camel)+ >]
{
leading,
..
} => leading.clone(),
)*
}
}
pub fn trailing(&self) -> Option<[< $db_name NodeId >]> {
match &self {
$(
| Node :: [< $($node_name:camel)+ >]
{
trailing,
..
} => trailing.clone(),
)*
}
}
}
pub mod item {
$(
#[derive(Clone, Debug)]
pub struct [< $($node_name:camel)+ >]
{
pub(crate) span: laburnum::Span,
pub(crate) leading: Option<super::[< $db_name NodeId >]>,
pub(crate) node: crate::$($node_name)::+ ,
pub(crate) trailing: Option<super::[< $db_name NodeId >]>,
}
)*
}
#[derive(Clone,Copy, PartialEq, Eq, Ord, PartialOrd)]
pub enum [< $db_name NodeId >] {
$(
[< $($node_name:camel)+ >]
{
span: laburnum::Span,
key: u64,
},
)*
}
impl [< $db_name NodeId >] {
pub fn span(&self) -> laburnum::Span {
match self {
$(
| [< $db_name NodeId >] :: [< $($node_name:camel)+ >] { span, .. } => *span,
)*
}
}
pub fn key(&self) -> u64 {
match self {
$(
| [< $db_name NodeId >] :: [< $($node_name:camel)+ >] { key, .. } => *key,
)*
}
}
}
impl std::hash::Hash for [< $db_name NodeId >] {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
match self {
$(
| [< $db_name NodeId >] :: [< $($node_name:camel)+ >] { span, .. } => span.hash(state),
)*
}
}
}
impl std::fmt::Display for [< $db_name NodeId >] {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
$(
| [< $db_name NodeId >] :: [< $($node_name:camel)+ >] { span, key } => {
write!(f,
"[{}NodeId({span})::{ty}({key})]",
stringify!($db_name),
ty=stringify!(
[< $($node_name:camel)+ >]
),
)
},
)*
}
}
}
impl std::fmt::Debug for [< $db_name NodeId >] {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
$(
| [< $db_name NodeId >] :: [< $($node_name:camel)+ >] { span, key } => {
write!(f,
"[{}NodeId({span})::{ty}({key})]",
stringify!($db_name),
ty=stringify!(
[< $($node_name:camel)+ >]
),
)},
)*
}
}
}
impl [< $db_name NodeId >] {
pub fn load<'node, 'id: 'node, 'state: 'id>(
&'id self,
state: &'state mut [< $db_name State >]<'_>,
) -> Option<Node> {
state.[< $db_name:snake >]().get(*self)
}
}
impl bluegum::Bluegum for [< $db_name NodeId >] {
fn node(&self, b: &mut bluegum::Builder) {
match self {
$(
| [< $db_name NodeId >] :: [< $($node_name:camel)+ >] { span, key } => {
b.name(concat!(stringify!($db_name), "NodeId"))
.debug("ty", stringify!([< $($node_name:camel)+ >]))
.debug("span",span)
.debug("key", key);
},
)*
}
}
}
#[derive(Clone, Debug)]
pub struct [< $db_name Checkpoint >] {
spans_len: usize,
$(
[<node_ $($node_name:snake)_+ _len>]: usize,
)*
}
#[derive(Clone, Debug, Default)]
pub struct [< $db_name NodeDbParser >] {
pub spans: Vec<(laburnum::Span, [< $db_name NodeId >])>,
$(
pub [<node_ $($node_name:snake)_+ >]: Vec<item::[< $($node_name:camel)+ >]>,
)*
}
#[derive(Clone, Debug)]
pub struct [< $db_name NodeDbQuery >] {
pub spans: indexmap::IndexMap<laburnum::Span, [< $db_name NodeId >]>,
$(
pub [<node_ $($node_name:snake)_+ >]: Vec<item::[< $($node_name:camel)+ >]>,
)*
}
impl [< $db_name NodeDbParser >] {
pub fn new() -> Self {
Self::default()
}
pub fn into_query(self) -> [< $db_name NodeDbQuery >] {
[< $db_name NodeDbQuery >] {
spans: self.spans.into_iter().collect(),
$( [<node_ $($node_name:snake)_+ >]: self.[<node_ $($node_name:snake)_+ >], )*
}
}
pub fn checkpoint(&self) -> [< $db_name Checkpoint >] {
[< $db_name Checkpoint >] {
spans_len: self.spans.len(),
$( [<node_ $($node_name:snake)_+ _len>]: self.[<node_ $($node_name:snake)_+ >].len(), )*
}
}
pub fn restore(&mut self, cp: &[< $db_name Checkpoint >]) {
self.spans.truncate(cp.spans_len);
$( self.[<node_ $($node_name:snake)_+ >].truncate(cp.[<node_ $($node_name:snake)_+ _len>]); )*
}
}
impl [< $db_name NodeDbQuery >] {
pub fn lookup_by_span(&self, span: laburnum::Span) -> Option<[< $db_name NodeId >]> {
self.spans.get(&span).copied()
}
}
pub trait [< $db_name NodeDbRead >] {
$(
fn [<node_ $($node_name:snake)_+ >](&self) -> &[item::[< $($node_name:camel)+ >]];
)*
fn get(&self, id: [< $db_name NodeId >]) -> Option<Node> {
match id {
$(
| [< $db_name NodeId >]::[< $($node_name:camel)+ >] { key, .. } => {
self.[<node_ $($node_name:snake)_+ >]().get(key as usize).map(|nd| crate::node::Node::[< $($node_name:camel)+ >]{
span: nd.span,
node: nd.node.clone(),
leading: nd.leading.clone(),
trailing: nd.trailing.clone(),
})
},
)*
}
}
fn print_nodes(&self) -> String {
let mut output = String::new();
$(
let nodes = self.[<node_ $($node_name:snake)_+ >]();
if !nodes.is_empty() {
output.push_str(
&format!(
"\n--{:-<100}\n",
format!(" {} ", stringify!([< $($node_name:camel)+ >]))
)
);
}
for (i, node) in nodes.iter().enumerate() {
output.push_str(&format!("{:03}: {} — {:?}\n", i, node.span, node))
}
)*
output
}
}
impl [< $db_name NodeDbRead >] for [< $db_name NodeDbParser >] {
$(
fn [<node_ $($node_name:snake)_+ >](&self) -> &[item::[< $($node_name:camel)+ >]] {
&self.[<node_ $($node_name:snake)_+ >]
}
)*
}
impl [< $db_name NodeDbRead >] for [< $db_name NodeDbQuery >] {
$(
fn [<node_ $($node_name:snake)_+ >](&self) -> &[item::[< $($node_name:camel)+ >]] {
&self.[<node_ $($node_name:snake)_+ >]
}
)*
}
pub trait [< $db_name ParserMapExtraExt >]<'tokens, I, E>
where
I: chumsky::input::Input<'tokens, Span = chumsky::span::SimpleSpan>,
E: chumsky::extra::ParserExtra<'tokens, I, State = [< $db_name State >]<'tokens>>,
{
$(
fn [<insert_ $($node_name:snake)_+ >]<F>(
&mut self,
leading: Option<[< $db_name NodeId >]>,
node: F,
trailing: Option<[< $db_name NodeId >]>,
) -> [< $db_name NodeId >]
where
F: FnOnce(laburnum::Span, &mut [< $db_name State >]<'tokens>) -> crate::$($node_name)::+;
)*
}
impl<'tokens, 'b, I, E> [< $db_name ParserMapExtraExt >]<'tokens, I, E>
for chumsky::input::MapExtra<'tokens, 'b, I, E>
where
I: chumsky::input::Input<'tokens, Span = chumsky::span::SimpleSpan>,
E: chumsky::extra::ParserExtra<'tokens, I, State = [< $db_name State >]<'tokens>>,
Self: laburnum::chumsky::LaburnumSpanExt<'tokens, 'b, I, E>,
{
$(
fn [<insert_ $($node_name:snake)_+ >]<F>(
&mut self,
leading: Option<[< $db_name NodeId >]>,
f: F,
trailing: Option<[< $db_name NodeId >]>,
) -> [< $db_name NodeId >]
where
F: FnOnce(laburnum::Span, &mut [< $db_name State >]<'tokens>) -> crate::$($node_name)::+,
{
let span = self.create_span();
let state = self.state();
let node = f(span, &mut *state);
let db = state.[< $db_name:snake _mut >]();
let existing_key = db.[<node_ $($node_name:snake)_+ >]
.iter()
.enumerate()
.rev()
.find(|(_, n)| n.span == node.span)
.map(|(i, _)| i as u64);
let next_key = if let Some(key) = existing_key {
db.[<node_ $($node_name:snake)_+ >][key as usize] = item::[< $($node_name:camel)+ >] {
span,
node,
leading,
trailing,
};
key
} else {
let next_key = db.[<node_ $($node_name:snake)_+ >].len();
db.[<node_ $($node_name:snake)_+ >].push(item::[< $($node_name:camel)+ >] {
span,
node,
leading,
trailing,
});
next_key as u64
};
let id = [< $db_name NodeId >]::[< $($node_name:camel)+ >] {
span,
key: next_key,
};
db.spans.push((span, id));
id
}
)*
}
pub enum [< $db_name State >]<'tokens> {
Detect {
inner: laburnum::chumsky::State<'tokens>,
[< $db_name:snake >]: [< $db_name NodeDbParser >],
source_key: laburnum::SourceKey,
did_see_error: bool,
},
Collect {
inner: laburnum::chumsky::State<'tokens>,
[< $db_name:snake >]: [< $db_name NodeDbParser >],
source_key: laburnum::SourceKey,
},
}
impl std::fmt::Debug for [< $db_name State >]<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
| Self::Detect {
[< $db_name:snake >],
source_key,
did_see_error,
..
} => f
.debug_struct(concat!(stringify!($db_name), "State::Detect"))
.field(stringify!([< $db_name:snake >]), [< $db_name:snake >])
.field("source_key", source_key)
.field("did_see_error", did_see_error)
.finish(),
| Self::Collect {
[< $db_name:snake >], source_key, ..
} => f
.debug_struct(concat!(stringify!($db_name), "State::Collect"))
.field(stringify!([< $db_name:snake >]), [< $db_name:snake >])
.field("source_key", source_key)
.finish(),
}
}
}
impl<'tokens> [< $db_name State >]<'tokens> {
pub fn new_detect(
source_key: laburnum::SourceKey,
span_cache: &'tokens mut laburnum::SpanCache,
) -> Self {
Self::Detect {
inner: laburnum::chumsky::State::new(source_key, span_cache),
[< $db_name:snake >]: [< $db_name NodeDbParser >]::new(),
source_key,
did_see_error: false,
}
}
pub fn new_collect(
source_key: laburnum::SourceKey,
span_cache: &'tokens mut laburnum::SpanCache,
) -> Self {
Self::Collect {
inner: laburnum::chumsky::State::new(source_key, span_cache),
[< $db_name:snake >]: [< $db_name NodeDbParser >]::new(),
source_key,
}
}
pub fn inner(&self) -> &laburnum::chumsky::State<'tokens> {
match self {
| Self::Detect { inner, .. } | Self::Collect { inner, .. } => inner,
}
}
pub fn inner_mut(&mut self) -> &mut laburnum::chumsky::State<'tokens> {
match self {
| Self::Detect { inner, .. } | Self::Collect { inner, .. } => inner,
}
}
pub fn [< $db_name:snake >](&self) -> &[< $db_name NodeDbParser >] {
match self {
| Self::Detect { [< $db_name:snake >], .. } | Self::Collect { [< $db_name:snake >], .. } => [< $db_name:snake >],
}
}
pub fn [< $db_name:snake _mut >](&mut self) -> &mut [< $db_name NodeDbParser >] {
match self {
| Self::Detect { [< $db_name:snake >], .. } | Self::Collect { [< $db_name:snake >], .. } => [< $db_name:snake >],
}
}
pub fn source_key(&self) -> laburnum::SourceKey {
match self {
| Self::Detect { source_key, .. } | Self::Collect { source_key, .. } => {
*source_key
},
}
}
pub fn span_cache(&self) -> &laburnum::SpanCache {
self.inner().span_cache
}
pub fn mark_error(&mut self) {
if let Self::Detect { did_see_error, .. } = self {
*did_see_error = true;
}
}
pub fn did_see_error(&self) -> bool {
match self {
| Self::Detect { did_see_error, .. } => *did_see_error,
| Self::Collect { .. } => false,
}
}
pub fn finish(self) -> std::sync::Arc<[< $db_name NodeDbQuery >]> {
match self {
| Self::Detect { [< $db_name:snake >], .. } | Self::Collect { [< $db_name:snake >], .. } => {
std::sync::Arc::new([< $db_name:snake >].into_query())
},
}
}
pub fn print_tree(
&self,
source: &str,
span_cache: &laburnum::SpanCache,
root: [< $db_name NodeId >],
) -> String {
let p = Printer::new(self.[< $db_name:snake >](), source, span_cache, root);
p.print()
}
pub fn print_nodes(&self) -> String {
self.[< $db_name:snake >]().print_nodes()
}
pub fn syntax_tree(&self, root: [< $db_name NodeId >]) -> Option<Node> {
self.get_syntax_node(root)
}
pub fn get_syntax_node(&self, id: [< $db_name NodeId >]) -> Option<Node> {
self.[< $db_name:snake >]().get(id)
}
pub fn get_syntax_node_mut(
&mut self,
_id: [< $db_name NodeId >],
) -> Option<&mut Node> {
None
}
}
impl<'tokens, I: chumsky::input::Input<'tokens>>
chumsky::inspector::Inspector<'tokens, I> for [< $db_name State >]<'tokens>
{
type Checkpoint = [< $db_name Checkpoint >];
#[inline(always)]
fn on_token(
&mut self,
_token: &<I as chumsky::input::Input<'tokens>>::Token,
) {
}
#[inline(always)]
fn on_save<'parse>(
&self,
_cursor: &chumsky::input::Cursor<'tokens, 'parse, I>,
) -> Self::Checkpoint {
self.[< $db_name:snake >]().checkpoint()
}
#[inline(always)]
fn on_rewind<'parse>(
&mut self,
checkpoint: &chumsky::input::Checkpoint<
'tokens,
'parse,
I,
Self::Checkpoint,
>,
) {
self.[< $db_name:snake _mut >]().restore(checkpoint.inspector());
}
}
impl laburnum::chumsky::SpanCreator for [< $db_name State >]<'_> {
fn span_from(&mut self, span: chumsky::prelude::SimpleSpan) -> laburnum::Span {
self
.inner_mut()
.span_cache
.create_span(span.start, span.end - span.start)
}
}
pub struct Printer<'state> {
nodes: &'state (dyn [< $db_name NodeDbRead >] + 'state),
source: &'state str,
span_cache: &'state laburnum::SpanCache,
root: [< $db_name NodeId >],
}
impl<'state> Printer<'state> {
pub fn new(
nodes: &'state (impl [< $db_name NodeDbRead >] + 'state),
source: &'state str,
span_cache: &'state laburnum::SpanCache,
root: [< $db_name NodeId >],
) -> Self {
Self {
nodes,
source,
span_cache,
root,
}
}
pub fn print(self) -> String {
let mut p = bluegum::Printer::new(bluegum::Styles {
..bluegum::Styles::default()
})
.set_title(concat!(stringify!($db_name), " Syntax Tree"));
p.render(&self).with_color().to_string()
}
pub fn print_plain(self) -> String {
let mut p = bluegum::Printer::new(bluegum::Styles {
..bluegum::Styles::default()
})
.set_title(concat!(stringify!($db_name), " Syntax Tree"));
p.render(&self).strip_color().to_string()
}
pub fn get(&self, id: [< $db_name NodeId >]) -> Option<Node> {
self.nodes.get(id)
}
pub fn span_text(&self, span: &laburnum::Span) -> &str {
span.text(self.span_cache, self.source).unwrap_or("")
}
}
impl<'state> bluegum::Bluegum for Printer<'state> {
fn node(&self, b: &mut bluegum::Builder) {
b.name(env!("CARGO_PKG_NAME"));
b.add_node_with_state(self, concat!(stringify!($db_name), " Syntax Tree"), &self.root);
}
}
}
};
}
pub use define_node_db;