Skip to content

Commit

Permalink
Enforce can_contain in tree mutation methods
Browse files Browse the repository at this point in the history
Fix #9.
  • Loading branch information
mortenpi committed Jul 4, 2022
1 parent e515b48 commit 6610331
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 0 deletions.
5 changes: 5 additions & 0 deletions src/markdown.jl
Original file line number Diff line number Diff line change
Expand Up @@ -484,3 +484,8 @@ Base.:(==)(x::TableCell, y::TableCell) = (x.align == y.align) && (x.header == y.
# struct Backslash <: AbstractInline end
# struct SoftBreak <: AbstractInline end
# struct LineBreak <: AbstractInline end

struct InvalidChildException <: Exception
parent :: AbstractElement
child :: AbstractElement
end
9 changes: 9 additions & 0 deletions src/node.jl
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@ children.
"""
function Base.push!(children::NodeChildren{T}, child::T) where {T <: Node}
node = children.parent
assert_can_contain(node, child)
# append_child
# The child node is unlinked first
unlink!(child)
Expand Down Expand Up @@ -300,6 +301,7 @@ children.
"""
function Base.pushfirst!(children::NodeChildren{T}, child::T) where T
node = children.parent
assert_can_contain(node, child)
# prepend_child
# The child node is unlinked first
unlink!(child)
Expand Down Expand Up @@ -329,6 +331,7 @@ node. If `sibling` is part of another tree, then it is unlinked from that tree f
function insert_after!(node::Node, sibling::Node)
# Adds the sibling after this node:
isrootnode(node) && throw(ArgumentError("the reference node must not be a root node"))
assert_can_contain(node.parent, sibling)
# The sibling node is unlinked first:
unlink!(sibling)
# If there is a node after `node`, we point sibling to it:
Expand Down Expand Up @@ -359,6 +362,7 @@ root node. If `sibling` is part of another tree, then it is unlinked from that t
function insert_before!(node::T, sibling::T) where {T <: Node}
# Fallback method for insert_before!
isrootnode(node) && throw(ArgumentError("the reference node must not be a root node"))
assert_can_contain(node.parent, sibling)
# If this node is the first node, then we can prepend the sibling as a child
# to the parent node. Otherwise, we just insert it after the previous node.
if isnothing(node.previous)
Expand Down Expand Up @@ -397,3 +401,8 @@ function Base.:(==)(x::Node{T}, y::Node{T}) where T
end
return true
end

function assert_can_contain(parent::T, child::T) where {T <: Node}
can_contain(parent.element, child.element) && return nothing
throw(InvalidChildException(parent.element, child.element))
end
40 changes: 40 additions & 0 deletions test/invalidast.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using MarkdownAST: MarkdownAST, @ast, Node,
Document, Paragraph, Strong, Link, CodeBlock, HTMLBlock, Code,
InvalidChildException, insert_after!, insert_before!
using Test

@testset "Invalid AST" begin
# Document can only contain blocks, not inlines
@test_throws InvalidChildException (@ast Document() do
Link("", "")
end)
valid_document = @ast Document() do
Paragraph()
end
node_strong = Node(Strong())
@test_throws InvalidChildException push!(valid_document.children, node_strong)
@test_throws InvalidChildException pushfirst!(valid_document.children, node_strong)
@test_throws InvalidChildException insert_after!(first(valid_document.children), node_strong)
@test_throws InvalidChildException insert_before!(first(valid_document.children), node_strong)

# Paragraph can only contain inlines, but not blocks:
@test_throws InvalidChildException (@ast Paragraph() do
HTMLBlock("")
end)
valid_p = @ast Paragraph() do
"..."
end
node_codeblock = Node(CodeBlock("", ""))
@test_throws InvalidChildException push!(valid_p.children, node_codeblock)
@test_throws InvalidChildException pushfirst!(valid_p.children, node_codeblock)
@test_throws InvalidChildException insert_after!(first(valid_p.children), node_codeblock)
@test_throws InvalidChildException insert_before!(first(valid_p.children), node_codeblock)

# Some nodes can not have child nodes
@test_throws InvalidChildException (@ast CodeBlock("", "") do
"..."
end)
@test_throws InvalidChildException (@ast Code("...") do
"..."
end)
end
1 change: 1 addition & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ using Test
@testset "MarkdownAST" begin
include("markdown.jl")
include("node.jl")
include("invalidast.jl")
end

0 comments on commit 6610331

Please sign in to comment.