phase 1: core AST, spans, and Visitor
Define every node type in SPEC.md §6.4 — Document, 11 block variants, 15 inline variants (including the 4 link-related ones), plus ListItem / DefinitionItem / TableRow / TableCell, ListSymbol, CheckboxState, Keyword, LinkTarget, LinkKind. Every node carries a `Span` (line + column + byte offset, 0-indexed) per §6.5. Visitor: open-recursion trait with default `visit_*` bodies that delegate to `walk_*` free functions. Override at any granularity; call `walk_*` from your override to keep descending. P4 resolved by defining the trait now so Phase 5 (renderer) and Phase 7 (semantic tokens) can plug in without refactoring traversal. P3 resolved: AST string fields are owned `String`. Cow / Arc<str> can be revisited if the parser grows a zero-copy mode. Tests: visitor smoke test builds a heading + paragraph + list + table by hand and asserts exact visit counts (4 blocks, 12 inlines, 9 text leaves, 1 bold, 1 external link, 1 transclusion). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
//! Block-level AST nodes and their supporting types.
|
||||
//!
|
||||
//! See SPEC.md §6.4.
|
||||
|
||||
use super::inline::InlineNode;
|
||||
use super::span::Span;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum BlockNode {
|
||||
Heading(HeadingNode),
|
||||
Paragraph(ParagraphNode),
|
||||
HorizontalRule(HorizontalRuleNode),
|
||||
Blockquote(BlockquoteNode),
|
||||
Preformatted(PreformattedNode),
|
||||
MathBlock(MathBlockNode),
|
||||
List(ListNode),
|
||||
DefinitionList(DefinitionListNode),
|
||||
Table(TableNode),
|
||||
Comment(CommentNode),
|
||||
Error(ErrorNode),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct HeadingNode {
|
||||
pub span: Span,
|
||||
/// 1..=6 — invariant enforced by the parser, not the type.
|
||||
pub level: u8,
|
||||
pub children: Vec<InlineNode>,
|
||||
pub centered: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ParagraphNode {
|
||||
pub span: Span,
|
||||
pub children: Vec<InlineNode>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct HorizontalRuleNode {
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct BlockquoteNode {
|
||||
pub span: Span,
|
||||
pub children: Vec<BlockNode>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct PreformattedNode {
|
||||
pub span: Span,
|
||||
pub content: String,
|
||||
pub language: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct MathBlockNode {
|
||||
pub span: Span,
|
||||
pub content: String,
|
||||
pub environment: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ListNode {
|
||||
pub span: Span,
|
||||
pub ordered: bool,
|
||||
pub symbol: ListSymbol,
|
||||
pub items: Vec<ListItemNode>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ListItemNode {
|
||||
pub span: Span,
|
||||
pub symbol: ListSymbol,
|
||||
pub level: usize,
|
||||
pub checkbox: Option<CheckboxState>,
|
||||
pub children: Vec<InlineNode>,
|
||||
pub sublist: Option<ListNode>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum ListSymbol {
|
||||
Dash,
|
||||
Star,
|
||||
Hash,
|
||||
Numeric,
|
||||
NumericParen,
|
||||
AlphaParen,
|
||||
AlphaUpperParen,
|
||||
RomanParen,
|
||||
RomanUpperParen,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum CheckboxState {
|
||||
Empty,
|
||||
Quarter,
|
||||
Half,
|
||||
ThreeQuarters,
|
||||
Done,
|
||||
Rejected,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct DefinitionListNode {
|
||||
pub span: Span,
|
||||
pub items: Vec<DefinitionItemNode>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct DefinitionItemNode {
|
||||
pub span: Span,
|
||||
pub term: Option<Vec<InlineNode>>,
|
||||
pub definitions: Vec<Vec<InlineNode>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct TableNode {
|
||||
pub span: Span,
|
||||
pub rows: Vec<TableRowNode>,
|
||||
pub has_header: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct TableRowNode {
|
||||
pub span: Span,
|
||||
pub cells: Vec<TableCellNode>,
|
||||
pub is_header: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct TableCellNode {
|
||||
pub span: Span,
|
||||
pub children: Vec<InlineNode>,
|
||||
pub col_span: bool,
|
||||
pub row_span: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct CommentNode {
|
||||
pub span: Span,
|
||||
pub content: String,
|
||||
}
|
||||
|
||||
/// Resilient parse-failure node — emitted instead of aborting the document.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ErrorNode {
|
||||
pub span: Span,
|
||||
pub raw: String,
|
||||
pub message: String,
|
||||
}
|
||||
Reference in New Issue
Block a user