// The syntax tree: block level first, then the inlines inside the leaves.
//
// Every container holds its children by value, so a tree is an ordinary
// immutable value once parsing is done; the parser builds it out of mutable
// scratch structures that never escape.

///|
/// How a table column lines up its cells, taken from the delimiter row.
pub(all) enum Align {
  /// No colons in the delimiter row: the column follows the page.
  Default
  /// A colon on the left only.
  Left
  /// A colon on both ends.
  Center
  /// A colon on the right only.
  Right
}

///|
/// A heading, `level` counting the ATX `#` marks or the setext underline.
pub(all) struct Heading {
  level : Int32
  content : Array[Inline]
}

///|
/// A code block, fenced or indented. `info` is the word after the opening
/// fence, empty for indented code.
pub(all) struct Code {
  info : String
  text : String
  fenced : Bool
}

///|
/// What kind of list a `ListBlock` is, together with the marks needed to write
/// it back unchanged.
pub(all) enum ListKind {
  /// `-`, `+` or `*`.
  Bullet(Char)
  /// The first number, then `.` or `)`.
  Ordered(Int32, Char)
}

///|
/// Whether a list item is a task list item, and if so whether it is done.
pub(all) enum Task {
  /// An ordinary list item.
  Plain
  /// `[ ]`.
  Unchecked
  /// `[x]` or `[X]`.
  Checked
}

///|
/// One item of a list, with the task marker that introduced it if any.
pub(all) struct Item {
  blocks : Array[Block]
  task : Task
}

///|
/// A list, with its items and whether it is tight (no blank lines between
/// items, so their paragraphs are rendered without `

`). pub(all) struct ListBlock { kind : ListKind tight : Bool items : Array[Item] } ///| /// A table: the alignment of each column, the header row, then the body rows. /// A row holds one cell per column, and a cell holds the inlines inside it. pub(all) struct Table { align : Array[Align] head : Array[Array[Inline]] body : Array[Array[Array[Inline]]] } ///| /// A footnote, the body of one `[^label]:` definition. `label` is the label as /// it was written. `number` is the order the footnote is shown with, assigned in /// the order its first reference appears in the text; `backrefs` is how many /// references point at it. pub(all) struct Footnote { label : String blocks : Array[Block] /// The number the footnote is shown with, or 0 until the document is read. number : Int32 /// The references that point at the footnote, in the order they appear. backrefs : Int32 } ///| /// A footnote reference, `[^label]`, and where it lands: the definition's /// `label`, the `number` that definition is shown with, and `ref_ix`, the order /// of this reference among the ones that point at the definition. pub(all) struct FootnoteRef { /// The label of the definition the reference points at, as written. label : String /// The number the definition is shown with. number : Int32 /// The order of this reference among the ones pointing at the definition. ref_ix : Int32 } ///| /// A block level node. pub(all) enum Block { /// A heading of `level` 1 to 6. Heading(Heading) /// A paragraph, holding its inlines. Paragraph(Array[Inline]) /// A code block. Code(Code) /// A block quote, holding the blocks inside it. Quote(Array[Block]) /// A bullet or ordered list. List(ListBlock) /// A table. Table(Table) /// A block of raw HTML, which the renderer escapes unless asked not to. Html(String) /// A thematic break, the line of three or more `-`, `*` or `_`. Break /// The body of a footnote definition. Footnote(Footnote) } ///| /// A line break inside a paragraph. pub(all) enum Break { /// A newline in the source, which HTML renders as a space or a newline. Soft /// Two trailing spaces or a trailing backslash, which HTML renders as `
`. Hard } ///| /// The destination, title and content of a link or image. For an image the /// content is the alternative text. pub(all) struct Link { dest : String title : String content : Array[Inline] } ///| /// An inline level node. pub(all) enum Inline { /// Literal text. Text(String) /// A code span, already with its newlines turned into spaces. Code(String) /// Emphasis with a single `*` or `_`. Emphasis(Array[Inline]) /// Strong emphasis with a double `*` or `_`. Strong(Array[Inline]) /// A link. Link(Link) /// An image. Image(Link) /// Raw inline HTML. Html(String) /// Struck out text between tildes. Strike(Array[Inline]) /// A soft or hard line break. Break(Break) /// A footnote reference, `[^label]`, with the number of the definition it /// points at and its order among the references to that definition. FootnoteRef(FootnoteRef) } ///| /// A node as `walk` hands it to its visitor: either a block or an inline. pub(all) enum Node { /// A block level node, with everything inside it. Block(Block) /// An inline level node, with everything inside it. Inline(Inline) } ///| /// Visit every node of the document, each container before its children and /// siblings in order. pub fn walk(document : Document, visit : (Node) -> Unit) -> Unit { for block in document.blocks() { walk_block(block, visit) } } ///| fn walk_block(block : Block, visit : (Node) -> Unit) -> Unit { visit(Block(block)) match block { Heading(heading) => walk_inlines(heading.content, visit) Paragraph(content) => walk_inlines(content, visit) Quote(children) => for child in children { walk_block(child, visit) } List(list) => for item in list.items { for child in item.blocks { walk_block(child, visit) } } Table(table) => { for cell in table.head { walk_inlines(cell, visit) } for row in table.body { for cell in row { walk_inlines(cell, visit) } } } Footnote(footnote) => walk_blocks(footnote.blocks, visit) Code(_) | Html(_) | Break => () } } ///| fn walk_blocks(blocks : Array[Block], visit : (Node) -> Unit) -> Unit { for block in blocks { walk_block(block, visit) } } ///| fn walk_inlines(inlines : Array[Inline], visit : (Node) -> Unit) -> Unit { for inline in inlines { visit(Inline(inline)) match inline { Emphasis(children) | Strong(children) | Strike(children) => walk_inlines(children, visit) Link(link) | Image(link) => walk_inlines(link.content, visit) Text(_) | Code(_) | Html(_) | Break(_) | FootnoteRef(_) => () } } }