package ast import "encoding/json" // ListType contains bitwise or'ed flags for list and list item objects. type ListType int // These are the possible flag values for the ListItem renderer. // Multiple flag values may be ORed together. // These are mostly of interest if you are writing a new output format. const ( ListTypeOrdered ListType = 1 << iota ListTypeDefinition ListTypeTerm ListItemContainsBlock ListItemBeginningOfList // TODO: figure out if this is of any use now ListItemEndOfList ) // CellAlignFlags holds a type of alignment in a table cell. type CellAlignFlags int // These are the possible flag values for the table cell renderer. // Only a single one of these values will be used; they are not ORed together. // These are mostly of interest if you are writing a new output format. const ( TableAlignmentLeft CellAlignFlags = 1 << iota TableAlignmentRight TableAlignmentCenter = (TableAlignmentLeft | TableAlignmentRight) ) func (a CellAlignFlags) String() string { switch a { case TableAlignmentLeft: return "left" case TableAlignmentRight: return "right" case TableAlignmentCenter: return "center" default: return "" } } // DocumentMatters holds the type of a {front,main,back}matter in the document type DocumentMatters int // These are all possible Document divisions. const ( DocumentMatterNone DocumentMatters = iota DocumentMatterFront DocumentMatterMain DocumentMatterBack ) // CitationTypes holds the type of a citation, informative, normative or suppressed type CitationTypes int const ( CitationTypeNone CitationTypes = iota CitationTypeSuppressed CitationTypeInformative CitationTypeNormative ) // Node defines an ast node type Node interface { AsContainer() *Container AsLeaf() *Leaf GetParent() Node SetParent(newParent Node) GetChildren() []Node SetChildren(newChildren []Node) } // Container is a type of node that can contain children type Container struct { Parent Node `json:"-"` Children []Node Literal []byte // Text contents of the leaf nodes Content []byte // Markdown content of the block nodes *Attribute // Block level attribute } func (c *Container) MarshalJSON() ([]byte, error) { type ContainerJSON struct { Children []Node `json:"children"` Literal string `json:"literal"` *Attribute } var c1 ContainerJSON c1.Children = c.Children c1.Literal = string(c.Literal) c1.Attribute = c.Attribute return json.Marshal(&c1) } // AsContainer returns itself as *Container func (c *Container) AsContainer() *Container { return c } // AsLeaf returns nil func (c *Container) AsLeaf() *Leaf { return nil } // GetParent returns parent node func (c *Container) GetParent() Node { return c.Parent } // SetParent sets the parent node func (c *Container) SetParent(newParent Node) { c.Parent = newParent } // GetChildren returns children nodes func (c *Container) GetChildren() []Node { return c.Children } // SetChildren sets children node func (c *Container) SetChildren(newChildren []Node) { c.Children = newChildren } // Leaf is a type of node that cannot have children type Leaf struct { Parent Node `json:"-"` Literal []byte // Text contents of the leaf nodes Content []byte // Markdown content of the block nodes *Attribute // Block level attribute } func (c *Leaf) MarshalJSON() ([]byte, error) { type LeafJSON struct { Literal string `json:"literal"` } var c1 LeafJSON c1.Literal = string(c.Literal) return json.Marshal(&c1) } // AsContainer returns nil func (l *Leaf) AsContainer() *Container { return nil } // AsLeaf returns itself as *Leaf func (l *Leaf) AsLeaf() *Leaf { return l } // GetParent returns parent node func (l *Leaf) GetParent() Node { return l.Parent } // SetParent sets the parent nodd func (l *Leaf) SetParent(newParent Node) { l.Parent = newParent } // GetChildren returns nil because Leaf cannot have children func (l *Leaf) GetChildren() []Node { return nil } // SetChildren will panic becuase Leaf cannot have children func (l *Leaf) SetChildren(newChildren []Node) { panic("leaf node cannot have children") } // Document represents markdown document node, a root of ast type Document struct { Container } func (doc *Document) MarshalJSON() ([]byte, error) { children := doc.GetChildren() if len(children) != 0 { return json.Marshal(children) } return []byte("[]"), nil } // DocumentMatter represents markdown node that signals a document // division: frontmatter, mainmatter or backmatter. type DocumentMatter struct { Container Matter DocumentMatters } // BlockQuote represents markdown block quote node type BlockQuote struct { Container } func (c *BlockQuote) MarshalJSON() ([]byte, error) { type BlockQuoteJSON struct { Type string `json:"type"` Children []Node `json:"children"` Literal string `json:"literal"` *Attribute } var c1 BlockQuoteJSON c1.Children = c.Children c1.Literal = string(c.Literal) c1.Type = "blockquote" return json.Marshal(&c1) } // Aside represents an markdown aside node. type Aside struct { Container } // List represents markdown list node type List struct { Container ListFlags ListType Tight bool // Skip

s around list item data if true BulletChar byte // '*', '+' or '-' in bullet lists Delimiter byte // '.' or ')' after the number in ordered lists Start int // for ordered lists this indicates the starting number if > 0 RefLink []byte // If not nil, turns this list item into a footnote item and triggers different rendering IsFootnotesList bool // This is a list of footnotes } // ListItem represents markdown list item node type ListItem struct { Container ListFlags ListType Tight bool // Skip

s around list item data if true BulletChar byte // '*', '+' or '-' in bullet lists Delimiter byte // '.' or ')' after the number in ordered lists RefLink []byte // If not nil, turns this list item into a footnote item and triggers different rendering IsFootnotesList bool // This is a list of footnotes } // Paragraph represents markdown paragraph node type Paragraph struct { Container } func (c *Paragraph) MarshalJSON() ([]byte, error) { type ParagraphJSON struct { Type string `json:"type"` Children []Node `json:"children"` } var c1 ParagraphJSON c1.Children = c.Children c1.Type = "paragraph" return json.Marshal(&c1) } // Math represents markdown MathAjax inline node type Math struct { Leaf } // MathBlock represents markdown MathAjax block node type MathBlock struct { Container } // Heading represents markdown heading node type Heading struct { Container Level int // This holds the heading level number HeadingID string // This might hold heading ID, if present IsTitleblock bool // Specifies whether it's a title block IsSpecial bool // We are a special heading (starts with .#) } func (c *Heading) MarshalJSON() ([]byte, error) { type HeadingJSON struct { Type string `json:"type"` Children []Node `json:"children"` Literal string `json:"literal"` Level int `json:"level"` IsTitleblock bool `json:"isTitleBlock"` *Attribute } var c1 HeadingJSON c1.Children = c.Children c1.Literal = string(c.Literal) c1.Attribute = c.Attribute c1.Type = "heading" c1.Level = c.Level c1.IsTitleblock = c.IsTitleblock return json.Marshal(&c1) } // HorizontalRule represents markdown horizontal rule node type HorizontalRule struct { Leaf } // Emph represents markdown emphasis node type Emph struct { Leaf } func (c *Emph) MarshalJSON() ([]byte, error) { type EmphJSON struct { Type string `json:"type"` Literal string `json:"literal"` *Attribute } var c1 EmphJSON c1.Literal = string(c.Literal) c1.Attribute = c.Attribute c1.Type = "emph" return json.Marshal(&c1) } type StatusTag struct { Leaf } func (c *StatusTag) MarshalJSON() ([]byte, error) { type StatusTagJSON struct { Type string `json:"type"` Literal string `json:"literal"` } var c1 StatusTagJSON c1.Literal = string(c.Literal) c1.Type = "status-tag" return json.Marshal(&c1) } // Strong represents markdown strong node type Strong struct { Leaf } func (c *Strong) MarshalJSON() ([]byte, error) { type StrongJSON struct { Type string `json:"type"` Literal string `json:"literal"` *Attribute } var c1 StrongJSON c1.Literal = string(c.Literal) c1.Attribute = c.Attribute c1.Type = "strong" return json.Marshal(&c1) } // Del represents markdown del node type Del struct { Leaf } // Link represents markdown link node type Link struct { Container Destination []byte // Destination is what goes into a href Title []byte // Title is the tooltip thing that goes in a title attribute NoteID int // NoteID contains a serial number of a footnote, zero if it's not a footnote Footnote Node // If it's a footnote, this is a direct link to the footnote Node. Otherwise nil. DeferredID []byte // If a deferred link this holds the original ID. } func (c *Link) MarshalJSON() ([]byte, error) { type LinkJSON struct { Type string `json:"type"` Children []Node `json:"children"` Literal string `json:"literal"` Title string `json:"title"` Destination string `json:"destination"` *Attribute } var c1 LinkJSON c1.Children = c.Children c1.Literal = string(c.Literal) c1.Attribute = c.Attribute c1.Title = string(c.Title) c1.Destination = string(c.Destination) c1.Type = "link" return json.Marshal(&c1) } // CrossReference is a reference node. type CrossReference struct { Container Destination []byte // Destination is where the reference points to } // Citation is a citation node. type Citation struct { Leaf Destination [][]byte // Destination is where the citation points to. Multiple ones are allowed. Type []CitationTypes // 1:1 mapping of destination and citation type Suffix [][]byte // Potential citation suffix, i.e. [@!RFC1035, p. 144] } // Image represents markdown image node type Image struct { Container Destination []byte // Destination is what goes into a href Title []byte // Title is the tooltip thing that goes in a title attribute } // Text represents markdown text node type Text struct { Leaf } // HTMLBlock represents markdown html node type HTMLBlock struct { Leaf } // CodeBlock represents markdown code block node type CodeBlock struct { Leaf IsFenced bool // Specifies whether it's a fenced code block or an indented one Info []byte // This holds the info string FenceChar byte FenceLength int FenceOffset int } func (c *CodeBlock) MarshalJSON() ([]byte, error) { type CodeBlockJSON struct { Type string `json:"type"` Literal string `json:"literal"` *Attribute } var c1 CodeBlockJSON c1.Literal = string(c.Literal) c1.Type = "codeblock" c1.Attribute = c.Attribute return json.Marshal(&c1) } // Softbreak represents markdown softbreak node // Note: not used currently type Softbreak struct { Leaf } // Hardbreak represents markdown hard break node type Hardbreak struct { Leaf } // NonBlockingSpace represents markdown non-blocking space node type NonBlockingSpace struct { Leaf } // Code represents markdown code node type Code struct { Leaf } func (c *Code) MarshalJSON() ([]byte, error) { type CodeJSON struct { Type string `json:"type"` Literal string `json:"literal"` } var c1 CodeJSON c1.Literal = string(c.Literal) c1.Type = "code" return json.Marshal(&c1) } // HTMLSpan represents markdown html span node type HTMLSpan struct { Leaf } // Table represents markdown table node type Table struct { Container } // TableCell represents markdown table cell node type TableCell struct { Container IsHeader bool // This tells if it's under the header row Align CellAlignFlags // This holds the value for align attribute } // TableHeader represents markdown table head node type TableHeader struct { Container } // TableBody represents markdown table body node type TableBody struct { Container } // TableRow represents markdown table row node type TableRow struct { Container } // TableFooter represents markdown table foot node type TableFooter struct { Container } // Caption represents a figure, code or quote caption type Caption struct { Container } // CaptionFigure is a node (blockquote or codeblock) that has a caption type CaptionFigure struct { Container HeadingID string // This might hold heading ID, if present } // Callout is a node that can exist both in text (where it is an actual node) and in a code block. type Callout struct { Leaf ID []byte // number of this callout } // Index is a node that contains an Index item and an optional, subitem. type Index struct { Leaf Primary bool Item []byte Subitem []byte ID string // ID of the index } // Subscript is a subscript node type Subscript struct { Leaf } // Subscript is a superscript node type Superscript struct { Leaf } // Footnotes is a node that contains all footnotes type Footnotes struct { Container } func removeNodeFromArray(a []Node, node Node) []Node { n := len(a) for i := 0; i < n; i++ { if a[i] == node { return append(a[:i], a[i+1:]...) } } return nil } // AppendChild appends child to children of parent // It panics if either node is nil. func AppendChild(parent Node, child Node) { RemoveFromTree(child) child.SetParent(parent) newChildren := append(parent.GetChildren(), child) parent.SetChildren(newChildren) } // RemoveFromTree removes this node from tree func RemoveFromTree(n Node) { if n.GetParent() == nil { return } // important: don't clear n.Children if n has no parent // we're called from AppendChild and that might happen on a node // that accumulated Children but hasn't been inserted into the tree n.SetChildren(nil) p := n.GetParent() newChildren := removeNodeFromArray(p.GetChildren(), n) if newChildren != nil { p.SetChildren(newChildren) } } // GetLastChild returns last child of node n // It's implemented as stand-alone function to keep Node interface small func GetLastChild(n Node) Node { a := n.GetChildren() if len(a) > 0 { return a[len(a)-1] } return nil } // GetFirstChild returns first child of node n // It's implemented as stand-alone function to keep Node interface small func GetFirstChild(n Node) Node { a := n.GetChildren() if len(a) > 0 { return a[0] } return nil } // GetNextNode returns next sibling of node n (node after n) // We can't make it part of Container or Leaf because we loose Node identity func GetNextNode(n Node) Node { parent := n.GetParent() if parent == nil { return nil } a := parent.GetChildren() len := len(a) - 1 for i := 0; i < len; i++ { if a[i] == n { return a[i+1] } } return nil } // GetPrevNode returns previous sibling of node n (node before n) // We can't make it part of Container or Leaf because we loose Node identity func GetPrevNode(n Node) Node { parent := n.GetParent() if parent == nil { return nil } a := parent.GetChildren() len := len(a) for i := 1; i < len; i++ { if a[i] == n { return a[i-1] } } return nil } // WalkStatus allows NodeVisitor to have some control over the tree traversal. // It is returned from NodeVisitor and different values allow Node.Walk to // decide which node to go to next. type WalkStatus int const ( // GoToNext is the default traversal of every node. GoToNext WalkStatus = iota // SkipChildren tells walker to skip all children of current node. SkipChildren // Terminate tells walker to terminate the traversal. Terminate ) // NodeVisitor is a callback to be called when traversing the syntax tree. // Called twice for every node: once with entering=true when the branch is // first visited, then with entering=false after all the children are done. type NodeVisitor interface { Visit(node Node, entering bool) WalkStatus } // NodeVisitorFunc casts a function to match NodeVisitor interface type NodeVisitorFunc func(node Node, entering bool) WalkStatus // Walk traverses tree recursively func Walk(n Node, visitor NodeVisitor) WalkStatus { isContainer := n.AsContainer() != nil status := visitor.Visit(n, true) // entering if status == Terminate { // even if terminating, close container node if isContainer { visitor.Visit(n, false) } return status } if isContainer && status != SkipChildren { children := n.GetChildren() for _, n := range children { status = Walk(n, visitor) if status == Terminate { return status } } } if isContainer { status = visitor.Visit(n, false) // exiting if status == Terminate { return status } } return GoToNext } // Visit calls visitor function func (f NodeVisitorFunc) Visit(node Node, entering bool) WalkStatus { return f(node, entering) } // WalkFunc is like Walk but accepts just a callback function func WalkFunc(n Node, f NodeVisitorFunc) { visitor := NodeVisitorFunc(f) Walk(n, visitor) }