diff --git a/crates/djls-semantic/src/blocks.rs b/crates/djls-semantic/src/blocks.rs index 4b35418e..10906792 100644 --- a/crates/djls-semantic/src/blocks.rs +++ b/crates/djls-semantic/src/blocks.rs @@ -1,5 +1,4 @@ mod builder; mod grammar; -mod nodes; mod snapshot; mod tree; diff --git a/crates/djls-semantic/src/blocks/builder.rs b/crates/djls-semantic/src/blocks/builder.rs index f04b3b79..6d4a3cae 100644 --- a/crates/djls-semantic/src/blocks/builder.rs +++ b/crates/djls-semantic/src/blocks/builder.rs @@ -7,15 +7,15 @@ use djls_templates::Node; use super::grammar::CloseValidation; use super::grammar::TagClass; use super::grammar::TagIndex; -use super::nodes::BlockId; -use super::nodes::BlockNode; -use super::nodes::BranchKind; +use super::tree::BlockId; +use super::tree::BlockNode; use super::tree::BlockTree; +use super::tree::BranchKind; use crate::traits::SemanticModel; use crate::Db; #[derive(Debug, Clone)] -enum BlockSemantics { +enum BlockSemanticOp { AddRoot { id: BlockId, }, @@ -51,7 +51,7 @@ pub struct BlockTreeBuilder<'db> { index: &'db TagIndex, stack: Vec>, block_allocs: Vec<(Span, Option)>, - semantic_ops: Vec, + semantic_ops: Vec, } impl<'db> BlockTreeBuilder<'db> { @@ -88,10 +88,10 @@ impl<'db> BlockTreeBuilder<'db> { for op in self.semantic_ops { match op { - BlockSemantics::AddRoot { id } => { + BlockSemanticOp::AddRoot { id } => { tree.roots_mut().push(id); } - BlockSemantics::AddBranchNode { + BlockSemanticOp::AddBranchNode { target, tag, marker_span, @@ -108,7 +108,7 @@ impl<'db> BlockTreeBuilder<'db> { }, ); } - BlockSemantics::AddLeafNode { + BlockSemanticOp::AddLeafNode { target, label, span, @@ -116,7 +116,7 @@ impl<'db> BlockTreeBuilder<'db> { tree.blocks_mut() .push_node(target, BlockNode::Leaf { label, span }); } - BlockSemantics::AddErrorNode { + BlockSemanticOp::AddErrorNode { target, message, span, @@ -124,10 +124,10 @@ impl<'db> BlockTreeBuilder<'db> { tree.blocks_mut() .push_node(target, BlockNode::Error { message, span }); } - BlockSemantics::ExtendBlockSpan { id, span } => { + BlockSemanticOp::ExtendBlockSpan { id, span } => { tree.blocks_mut().extend_block(id, span); } - BlockSemantics::FinalizeSpanTo { id, end } => { + BlockSemanticOp::FinalizeSpanTo { id, end } => { tree.blocks_mut().finalize_block_span(id, end); } } @@ -150,14 +150,14 @@ impl<'db> BlockTreeBuilder<'db> { if let Some(parent_id) = parent { // Nested block - self.semantic_ops.push(BlockSemantics::AddBranchNode { + self.semantic_ops.push(BlockSemanticOp::AddBranchNode { target: parent_id, tag: tag_name.clone(), marker_span: span, body: container, kind: BranchKind::Opener, }); - self.semantic_ops.push(BlockSemantics::AddBranchNode { + self.semantic_ops.push(BlockSemanticOp::AddBranchNode { target: container, tag: tag_name.clone(), marker_span: span, @@ -167,8 +167,8 @@ impl<'db> BlockTreeBuilder<'db> { } else { // Root block self.semantic_ops - .push(BlockSemantics::AddRoot { id: container }); - self.semantic_ops.push(BlockSemantics::AddBranchNode { + .push(BlockSemanticOp::AddRoot { id: container }); + self.semantic_ops.push(BlockSemanticOp::AddBranchNode { target: container, tag: tag_name.clone(), marker_span: span, @@ -194,7 +194,7 @@ impl<'db> BlockTreeBuilder<'db> { } TagClass::Unknown => { if let Some(segment) = get_active_segment(&self.stack) { - self.semantic_ops.push(BlockSemantics::AddLeafNode { + self.semantic_ops.push(BlockSemanticOp::AddLeafNode { target: segment, label: tag_name, span, @@ -210,7 +210,7 @@ impl<'db> BlockTreeBuilder<'db> { while self.stack.len() > frame_idx + 1 { if let Some(unclosed) = self.stack.pop() { if let Some(parent) = unclosed.parent_body { - self.semantic_ops.push(BlockSemantics::AddErrorNode { + self.semantic_ops.push(BlockSemanticOp::AddErrorNode { target: parent, message: format!("Unclosed block '{}'", unclosed.opener_name), span: unclosed.opener_span, @@ -229,18 +229,18 @@ impl<'db> BlockTreeBuilder<'db> { CloseValidation::Valid => { // Finalize the last segment body to end just before the closer marker let content_end = span.start().saturating_sub(TagDelimiter::LENGTH_U32); - self.semantic_ops.push(BlockSemantics::FinalizeSpanTo { + self.semantic_ops.push(BlockSemanticOp::FinalizeSpanTo { id: frame.segment_body, end: content_end, }); // Extend to include closer - self.semantic_ops.push(BlockSemantics::ExtendBlockSpan { + self.semantic_ops.push(BlockSemanticOp::ExtendBlockSpan { id: frame.container_body, span, }); } CloseValidation::ArgumentMismatch { arg, expected, got } => { - self.semantic_ops.push(BlockSemantics::AddErrorNode { + self.semantic_ops.push(BlockSemanticOp::AddErrorNode { target: frame.segment_body, message: format!( "Argument '{arg}' mismatch: expected '{expected}', got '{got}'" @@ -250,7 +250,7 @@ impl<'db> BlockTreeBuilder<'db> { self.stack.push(frame); // Restore frame } CloseValidation::MissingRequiredArg { arg, expected } => { - self.semantic_ops.push(BlockSemantics::AddErrorNode { + self.semantic_ops.push(BlockSemanticOp::AddErrorNode { target: frame.segment_body, message: format!( "Missing required argument '{arg}': expected '{expected}'" @@ -260,7 +260,7 @@ impl<'db> BlockTreeBuilder<'db> { self.stack.push(frame); } CloseValidation::UnexpectedArg { arg, got } => { - self.semantic_ops.push(BlockSemantics::AddErrorNode { + self.semantic_ops.push(BlockSemanticOp::AddErrorNode { target: frame.segment_body, message: format!("Unexpected argument '{arg}' with value '{got}'"), span, @@ -270,7 +270,7 @@ impl<'db> BlockTreeBuilder<'db> { CloseValidation::NotABlock => { // Should not happen as we already classified it if let Some(segment) = get_active_segment(&self.stack) { - self.semantic_ops.push(BlockSemantics::AddErrorNode { + self.semantic_ops.push(BlockSemanticOp::AddErrorNode { target: segment, message: format!("Internal error: {opener_name} is not a block"), span, @@ -279,7 +279,7 @@ impl<'db> BlockTreeBuilder<'db> { } } } else if let Some(segment) = get_active_segment(&self.stack) { - self.semantic_ops.push(BlockSemantics::AddErrorNode { + self.semantic_ops.push(BlockSemanticOp::AddErrorNode { target: segment, message: format!("Unexpected closing tag '{opener_name}'"), span, @@ -295,7 +295,7 @@ impl<'db> BlockTreeBuilder<'db> { let segment_to_finalize = frame.segment_body; let container = frame.container_body; - self.semantic_ops.push(BlockSemantics::FinalizeSpanTo { + self.semantic_ops.push(BlockSemanticOp::FinalizeSpanTo { id: segment_to_finalize, end: content_end, }); @@ -304,7 +304,7 @@ impl<'db> BlockTreeBuilder<'db> { let new_segment_id = self.alloc_block_id(Span::new(body_start, 0), Some(container)); // Add the branch node for the new segment - self.semantic_ops.push(BlockSemantics::AddBranchNode { + self.semantic_ops.push(BlockSemanticOp::AddBranchNode { target: container, tag: tag_name.to_string(), marker_span: span, @@ -317,7 +317,7 @@ impl<'db> BlockTreeBuilder<'db> { let segment = frame.segment_body; let opener_name = frame.opener_name.clone(); - self.semantic_ops.push(BlockSemantics::AddErrorNode { + self.semantic_ops.push(BlockSemanticOp::AddErrorNode { target: segment, message: format!("'{tag_name}' is not valid in '{opener_name}'"), span, @@ -334,12 +334,12 @@ impl<'db> BlockTreeBuilder<'db> { if self.index.is_end_optional(&frame.opener_name) { // No explicit closer: finalize last segment to end of input (best-effort) // We do not know the real end; leave as-is and extend container by opener span only. - self.semantic_ops.push(BlockSemantics::ExtendBlockSpan { + self.semantic_ops.push(BlockSemanticOp::ExtendBlockSpan { id: frame.container_body, span: frame.opener_span, }); } else if let Some(parent) = frame.parent_body { - self.semantic_ops.push(BlockSemantics::AddErrorNode { + self.semantic_ops.push(BlockSemanticOp::AddErrorNode { target: parent, message: format!("Unclosed block '{}'", frame.opener_name), span: frame.opener_span, @@ -380,7 +380,7 @@ impl<'db> SemanticModel<'db> for BlockTreeBuilder<'db> { } Node::Comment { span, .. } => { if let Some(parent) = get_active_segment(&self.stack) { - self.semantic_ops.push(BlockSemantics::AddLeafNode { + self.semantic_ops.push(BlockSemanticOp::AddLeafNode { target: parent, label: "".into(), span, @@ -389,7 +389,7 @@ impl<'db> SemanticModel<'db> for BlockTreeBuilder<'db> { } Node::Variable { span, .. } => { if let Some(parent) = get_active_segment(&self.stack) { - self.semantic_ops.push(BlockSemantics::AddLeafNode { + self.semantic_ops.push(BlockSemanticOp::AddLeafNode { target: parent, label: "".into(), span, @@ -400,7 +400,7 @@ impl<'db> SemanticModel<'db> for BlockTreeBuilder<'db> { full_span, error, .. } => { if let Some(parent) = get_active_segment(&self.stack) { - self.semantic_ops.push(BlockSemantics::AddLeafNode { + self.semantic_ops.push(BlockSemanticOp::AddLeafNode { target: parent, label: error.to_string(), span: full_span, diff --git a/crates/djls-semantic/src/blocks/nodes.rs b/crates/djls-semantic/src/blocks/nodes.rs deleted file mode 100644 index 6359a9eb..00000000 --- a/crates/djls-semantic/src/blocks/nodes.rs +++ /dev/null @@ -1,159 +0,0 @@ -use djls_source::Span; -use serde::Serialize; - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize)] -pub struct BlockId(u32); - -impl BlockId { - pub fn new(id: u32) -> Self { - Self(id) - } - - pub fn id(self) -> u32 { - self.0 - } - - pub fn index(self) -> usize { - self.0 as usize - } -} - -#[derive(Clone, Debug, Default, Serialize)] -pub struct Blocks(Vec); - -impl Blocks { - pub fn get(&self, id: usize) -> &Region { - &self.0[id] - } -} - -impl IntoIterator for Blocks { - type Item = Region; - type IntoIter = std::vec::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} - -impl<'a> IntoIterator for &'a Blocks { - type Item = &'a Region; - type IntoIter = std::slice::Iter<'a, Region>; - - fn into_iter(self) -> Self::IntoIter { - self.0.iter() - } -} - -impl<'a> IntoIterator for &'a mut Blocks { - type Item = &'a mut Region; - type IntoIter = std::slice::IterMut<'a, Region>; - - fn into_iter(self) -> Self::IntoIter { - self.0.iter_mut() - } -} - -impl Blocks { - pub fn alloc(&mut self, span: Span, parent: Option) -> BlockId { - let id = BlockId(u32::try_from(self.0.len()).unwrap_or_default()); - self.0.push(Region::new(span, parent)); - id - } - - pub fn extend_block(&mut self, id: BlockId, span: Span) { - self.block_mut(id).extend_span(span); - } - - pub fn set_block_span(&mut self, id: BlockId, span: Span) { - self.block_mut(id).set_span(span); - } - - pub fn finalize_block_span(&mut self, id: BlockId, end: u32) { - let block = self.block_mut(id); - let start = block.span().start(); - block.set_span(Span::saturating_from_bounds_usize( - start as usize, - end as usize, - )); - } - - pub fn push_node(&mut self, target: BlockId, node: BlockNode) { - let span = node.span(); - self.extend_block(target, span); - self.block_mut(target).nodes.push(node); - } - - fn block_mut(&mut self, id: BlockId) -> &mut Region { - let idx = id.index(); - &mut self.0[idx] - } -} - -#[derive(Clone, Debug, Serialize)] -pub struct Region { - span: Span, - nodes: Vec, - parent: Option, -} - -impl Region { - fn new(span: Span, parent: Option) -> Self { - Self { - span, - nodes: Vec::new(), - parent, - } - } - - pub fn span(&self) -> &Span { - &self.span - } - - pub fn set_span(&mut self, span: Span) { - self.span = span; - } - - pub fn nodes(&self) -> &Vec { - &self.nodes - } - - fn extend_span(&mut self, span: Span) { - let opening = self.span.start().saturating_sub(span.start()); - let closing = span.end().saturating_sub(self.span.end()); - self.span = self.span.expand(opening, closing); - } -} - -#[derive(Clone, Debug, Serialize)] -pub enum BranchKind { - Opener, - Segment, -} - -#[derive(Clone, Debug, Serialize)] -pub enum BlockNode { - Leaf { - label: String, - span: Span, - }, - Branch { - tag: String, - marker_span: Span, - body: BlockId, - kind: BranchKind, - }, - Error { - message: String, - span: Span, - }, -} - -impl BlockNode { - fn span(&self) -> Span { - match self { - BlockNode::Leaf { span, .. } | BlockNode::Error { span, .. } => *span, - BlockNode::Branch { marker_span, .. } => *marker_span, - } - } -} diff --git a/crates/djls-semantic/src/blocks/snapshot.rs b/crates/djls-semantic/src/blocks/snapshot.rs index fd53c597..ed093913 100644 --- a/crates/djls-semantic/src/blocks/snapshot.rs +++ b/crates/djls-semantic/src/blocks/snapshot.rs @@ -3,10 +3,10 @@ use std::collections::HashSet; use djls_source::Span; use serde::Serialize; -use super::nodes::BlockId; -use super::nodes::BlockNode; -use super::nodes::BranchKind; +use super::tree::BlockId; +use super::tree::BlockNode; use super::tree::BlockTree; +use super::tree::BranchKind; // TODO: centralize salsa struct snapshots so this mess can be shared diff --git a/crates/djls-semantic/src/blocks/tree.rs b/crates/djls-semantic/src/blocks/tree.rs index 635defb1..8fc95b08 100644 --- a/crates/djls-semantic/src/blocks/tree.rs +++ b/crates/djls-semantic/src/blocks/tree.rs @@ -1,8 +1,6 @@ +use djls_source::Span; use serde::Serialize; -use super::nodes::BlockId; -use super::nodes::Blocks; - #[derive(Clone, Debug, Serialize)] pub struct BlockTree { roots: Vec, @@ -54,6 +52,163 @@ impl Default for BlockTree { } } +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize)] +pub struct BlockId(u32); + +impl BlockId { + pub fn new(id: u32) -> Self { + Self(id) + } + + pub fn id(self) -> u32 { + self.0 + } + + pub fn index(self) -> usize { + self.0 as usize + } +} + +#[derive(Clone, Debug, Default, Serialize)] +pub struct Blocks(Vec); + +impl Blocks { + pub fn get(&self, id: usize) -> &Region { + &self.0[id] + } +} + +impl IntoIterator for Blocks { + type Item = Region; + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl<'a> IntoIterator for &'a Blocks { + type Item = &'a Region; + type IntoIter = std::slice::Iter<'a, Region>; + + fn into_iter(self) -> Self::IntoIter { + self.0.iter() + } +} + +impl<'a> IntoIterator for &'a mut Blocks { + type Item = &'a mut Region; + type IntoIter = std::slice::IterMut<'a, Region>; + + fn into_iter(self) -> Self::IntoIter { + self.0.iter_mut() + } +} + +impl Blocks { + pub fn alloc(&mut self, span: Span, parent: Option) -> BlockId { + let id = BlockId(u32::try_from(self.0.len()).unwrap_or_default()); + self.0.push(Region::new(span, parent)); + id + } + + pub fn extend_block(&mut self, id: BlockId, span: Span) { + self.block_mut(id).extend_span(span); + } + + pub fn set_block_span(&mut self, id: BlockId, span: Span) { + self.block_mut(id).set_span(span); + } + + pub fn finalize_block_span(&mut self, id: BlockId, end: u32) { + let block = self.block_mut(id); + let start = block.span().start(); + block.set_span(Span::saturating_from_bounds_usize( + start as usize, + end as usize, + )); + } + + pub fn push_node(&mut self, target: BlockId, node: BlockNode) { + let span = node.span(); + self.extend_block(target, span); + self.block_mut(target).nodes.push(node); + } + + fn block_mut(&mut self, id: BlockId) -> &mut Region { + let idx = id.index(); + &mut self.0[idx] + } +} + +#[derive(Clone, Debug, Serialize)] +pub struct Region { + span: Span, + nodes: Vec, + parent: Option, +} + +impl Region { + fn new(span: Span, parent: Option) -> Self { + Self { + span, + nodes: Vec::new(), + parent, + } + } + + pub fn span(&self) -> &Span { + &self.span + } + + pub fn set_span(&mut self, span: Span) { + self.span = span; + } + + pub fn nodes(&self) -> &Vec { + &self.nodes + } + + fn extend_span(&mut self, span: Span) { + let opening = self.span.start().saturating_sub(span.start()); + let closing = span.end().saturating_sub(self.span.end()); + self.span = self.span.expand(opening, closing); + } +} + +#[derive(Clone, Debug, Serialize)] +pub enum BranchKind { + Opener, + Segment, +} + +#[derive(Clone, Debug, Serialize)] +pub enum BlockNode { + Leaf { + label: String, + span: Span, + }, + Branch { + tag: String, + marker_span: Span, + body: BlockId, + kind: BranchKind, + }, + Error { + message: String, + span: Span, + }, +} + +impl BlockNode { + fn span(&self) -> Span { + match self { + BlockNode::Leaf { span, .. } | BlockNode::Error { span, .. } => *span, + BlockNode::Branch { marker_span, .. } => *marker_span, + } + } +} + #[cfg(test)] mod tests { use std::sync::Arc;