Skip to content

Instantly share code, notes, and snippets.

@goerz
Created September 24, 2023 19:58
Show Gist options
  • Save goerz/3b1eb0fdd161abb84f592149d0ec7659 to your computer and use it in GitHub Desktop.
Save goerz/3b1eb0fdd161abb84f592149d0ec7659 to your computer and use it in GitHub Desktop.
using Pkg
Pkg.activate(temp=true)
Pkg.add("MarkdownAST")
import MarkdownAST
"""
replace(f::Function, root::Node)
Creates a copy of the tree where all child nodes of `root` are recursively
replaced by the result of `f(child)`.
The function `f(child::Node)` must return either a new `Node` to replace
`child` or a Vector of nodes that will be inserted as siblings, replacing
`child`.
Note that `replace` does not allow the construction of invalid trees, and
element replacements that require invalid parent-child relationships (e.g., a
block element as a child to an element expecting inlines) will throw an error.
# Example
The following snippet removes links from the given AST. That is, it replaces
`Link` nodes with their link text (which may contain nested inline markdown
elements):
```julia
new_mdast = replace(mdast) do node
if node.element isa MarkdownAST.Link
return [MarkdownAST.copy_tree(child) for child in node.children]
else
return node
end
end
```
"""
function Base.replace(f::Function, root::MarkdownAST.Node{M}) where M
new_root = MarkdownAST.Node{M}(root.element, deepcopy(root.meta))
for child in root.children
replaced_child = replace(f, child)
transformed = f(replaced_child)
if transformed isa MarkdownAST.Node
push!(new_root.children, transformed)
elseif transformed isa Vector
append!(new_root.children, transformed)
else
error("Function `f` in `replace(f, root::MarkdownAST.Node)` must return either a Node or a Vector of nodes, not $(repr(typeof(transformed)))")
end
end
return new_root
end
"""
replace!(f::Function, root::Node)
Acts like `replace(f, root)`, but modifies `root` in-place.
"""
function Base.replace!(f::Function, root::MarkdownAST.Node{M}) where M
new_root = replace(f, root)
while !isempty(root.children)
# `Base.empty!(root.children)` would be nice!
MarkdownAST.unlink!(first(root.children))
end
append!(root.children, new_root.children)
return root
end
## TEST ######################################################################
#
# As a test, we're resolving simple citation links in a format similar to
# https://juliadocs.org/DocumenterCitations.jl/stable/gallery/#Custom-style:-Citation-key-labels
#
# That test replaces a single Link node with a list of new inline nodes that
# mix text and links to a `references.md` page.
#
# Also, to test the simpler transformation of a node with a single new node, we
# replace Strong (bold) nodes with Emph (italic) nodes – This could also be
# donw with MarkdownAST.copy_tree directly, but it's just a test.
Pkg.add(url="https://github.com/JuliaDocs/Documenter.jl", rev="master")
import Markdown
import Documenter
MD = raw"""
# Quantum Control
**[Quantum optimal control](https://qutip.org/docs/latest/guide/guide-control.html)**
[BrumerShapiro2003;BrifNJP2010;KochJPCM2016;SolaAAMOP2018;MorzhinRMS2019;
Wilhelm2003.10132;KochEPJQT2022](@cite) attempts to steer a quantum system in
some desired way.
## Methods used
We use the following methods:
* *[Krotov's method](https://github.com/JuliaQuantumControl/Krotov.jl)*
[Krotov1996](@cite), and
* [**GRAPE** (*Gradient Ascent Pulse Engineering*)](https://github.com/JuliaQuantumControl/GRAPE.jl)
[KhanejaJMR2005;FouquieresJMR2011](@cite).
This concludes the document.
"""
function parse_md_string(mdsrc)
mdpage = Markdown.parse(mdsrc)
return convert(MarkdownAST.Node, mdpage)
end
mdast = parse_md_string(MD)
println("====== IN =======")
println("AS AST:")
@show mdast
println("AS TEXT:")
print(string(convert(Markdown.MD, mdast)))
println("=== TRANSFORM ===")
replace!(mdast) do node
if node.element == MarkdownAST.Link("@cite", "")
text = first(node.children).element.text # assume no nested markdown
keys = [strip(key) for key in split(text, ";")]
n = length(keys)
if n == 1
k = keys[1]
new_md = "[[$k]](references.md#$k)"
else
k1 = keys[1]
k2 = keys[end]
if n > 2
new_md = "[[$k1](references.md#$k1)-[$k2](references.md#$k2)]"
else
new_md = "[[$k1](references.md#$k1), [$k2](references.md#$k2)]"
end
end
return Documenter.mdparse(new_md; mode=:span)
# We probably wouldn't want to use `Documenter`, but it shouldn't be
# hard to copy in a stripped-down version of `mdparse` here.
elseif node.element == MarkdownAST.Strong()
# Not sure if `copy_tree(f, node)` is really the most elegant way to do
# this, but I wanted to try out how `copy_tree` can modify a node's
# `element`.
return MarkdownAST.copy_tree(node) do node, element
element == MarkdownAST.Strong() ? MarkdownAST.Emph() : element
end
else
return node
end
end
println("====== OUT =======")
println("AS AST:")
@show mdast
println("AS TEXT:")
print(string(convert(Markdown.MD, mdast)))
println("====== END =======")
# TEST 2: delete links (example from the docstring) ##########################
println("\n\n=====================================")
println("TEST2: ORIGINAL MD WITH LINKS REMOVED")
mdast = parse_md_string(MD)
replace!(mdast) do node
if node.element isa MarkdownAST.Link
return [MarkdownAST.copy_tree(child) for child in node.children]
else
return node
end
end
print(string(convert(Markdown.MD, mdast)))
println("====== END =======")
@goerz
Copy link
Author

goerz commented Sep 24, 2023

Output:

====== IN =======
AS AST:
mdast = @ast MarkdownAST.Document() do
  MarkdownAST.Heading(1) do
    MarkdownAST.Text("Quantum Control")
  end
  MarkdownAST.Paragraph() do
    MarkdownAST.Strong() do
      MarkdownAST.Link("https://qutip.org/docs/latest/guide/guide-control.html", "") do
        MarkdownAST.Text("Quantum optimal control")
      end
    end
    MarkdownAST.Text(" ")
    MarkdownAST.Link("@cite", "") do
      MarkdownAST.Text("BrumerShapiro2003;BrifNJP2010;KochJPCM2016;SolaAAMOP2018;MorzhinRMS2019; Wilhelm2003.10132;KochEPJQT2022")
    end
    MarkdownAST.Text(" attempts to steer a quantum system in some desired way.")
  end
  MarkdownAST.Heading(2) do
    MarkdownAST.Text("Methods used")
  end
  MarkdownAST.Paragraph() do
    MarkdownAST.Text("We use the following methods:")
  end
  MarkdownAST.List(:bullet, true) do
    MarkdownAST.Item() do
      MarkdownAST.Paragraph() do
        MarkdownAST.Emph() do
          MarkdownAST.Link("https://github.com/JuliaQuantumControl/Krotov.jl", "") do
            MarkdownAST.Text("Krotov's method")
          end
        end
        MarkdownAST.Text(" ")
        MarkdownAST.Link("@cite", "") do
          MarkdownAST.Text("Krotov1996")
        end
        MarkdownAST.Text(", and")
      end
    end
    MarkdownAST.Item() do
      MarkdownAST.Paragraph() do
        MarkdownAST.Link("https://github.com/JuliaQuantumControl/GRAPE.jl", "") do
          MarkdownAST.Strong() do
            MarkdownAST.Text("GRAPE")
          end
          MarkdownAST.Text(" (")
          MarkdownAST.Emph() do
            MarkdownAST.Text("Gradient Ascent Pulse Engineering")
          end
          MarkdownAST.Text(")")
        end
        MarkdownAST.Text(" ")
        MarkdownAST.Link("@cite", "") do
          MarkdownAST.Text("KhanejaJMR2005;FouquieresJMR2011")
        end
        MarkdownAST.Text(".")
      end
    end
  end
  MarkdownAST.Paragraph() do
    MarkdownAST.Text("This concludes the document.")
  end
end

AS TEXT:
# Quantum Control

**[Quantum optimal control](https://qutip.org/docs/latest/guide/guide-control.html)** [BrumerShapiro2003;BrifNJP2010;KochJPCM2016;SolaAAMOP2018;MorzhinRMS2019; Wilhelm2003.10132;KochEPJQT2022](@cite) attempts to steer a quantum system in some desired way.

## Methods used

We use the following methods:

  * *[Krotov's method](https://github.com/JuliaQuantumControl/Krotov.jl)* [Krotov1996](@cite), and
  * [**GRAPE** (*Gradient Ascent Pulse Engineering*)](https://github.com/JuliaQuantumControl/GRAPE.jl) [KhanejaJMR2005;FouquieresJMR2011](@cite).

This concludes the document.
=== TRANSFORM ===
====== OUT =======
AS AST:
mdast = @ast MarkdownAST.Document() do
  MarkdownAST.Heading(1) do
    MarkdownAST.Text("Quantum Control")
  end
  MarkdownAST.Paragraph() do
    MarkdownAST.Emph() do
      MarkdownAST.Link("https://qutip.org/docs/latest/guide/guide-control.html", "") do
        MarkdownAST.Text("Quantum optimal control")
      end
    end
    MarkdownAST.Text(" ")
    MarkdownAST.Text("[")
    MarkdownAST.Link("references.md#BrumerShapiro2003", "") do
      MarkdownAST.Text("BrumerShapiro2003")
    end
    MarkdownAST.Text("-")
    MarkdownAST.Link("references.md#KochEPJQT2022", "") do
      MarkdownAST.Text("KochEPJQT2022")
    end
    MarkdownAST.Text("]")
    MarkdownAST.Text(" attempts to steer a quantum system in some desired way.")
  end
  MarkdownAST.Heading(2) do
    MarkdownAST.Text("Methods used")
  end
  MarkdownAST.Paragraph() do
    MarkdownAST.Text("We use the following methods:")
  end
  MarkdownAST.List(:bullet, true) do
    MarkdownAST.Item() do
      MarkdownAST.Paragraph() do
        MarkdownAST.Emph() do
          MarkdownAST.Link("https://github.com/JuliaQuantumControl/Krotov.jl", "") do
            MarkdownAST.Text("Krotov's method")
          end
        end
        MarkdownAST.Text(" ")
        MarkdownAST.Link("references.md#Krotov1996", "") do
          MarkdownAST.Text("[Krotov1996]")
        end
        MarkdownAST.Text(", and")
      end
    end
    MarkdownAST.Item() do
      MarkdownAST.Paragraph() do
        MarkdownAST.Link("https://github.com/JuliaQuantumControl/GRAPE.jl", "") do
          MarkdownAST.Emph() do
            MarkdownAST.Text("GRAPE")
          end
          MarkdownAST.Text(" (")
          MarkdownAST.Emph() do
            MarkdownAST.Text("Gradient Ascent Pulse Engineering")
          end
          MarkdownAST.Text(")")
        end
        MarkdownAST.Text(" ")
        MarkdownAST.Text("[")
        MarkdownAST.Link("references.md#KhanejaJMR2005", "") do
          MarkdownAST.Text("KhanejaJMR2005")
        end
        MarkdownAST.Text(", ")
        MarkdownAST.Link("references.md#FouquieresJMR2011", "") do
          MarkdownAST.Text("FouquieresJMR2011")
        end
        MarkdownAST.Text("]")
        MarkdownAST.Text(".")
      end
    end
  end
  MarkdownAST.Paragraph() do
    MarkdownAST.Text("This concludes the document.")
  end
end

AS TEXT:
# Quantum Control

*[Quantum optimal control](https://qutip.org/docs/latest/guide/guide-control.html)* [[BrumerShapiro2003](references.md#BrumerShapiro2003)-[KochEPJQT2022](references.md#KochEPJQT2022)] attempts to steer a quantum system in some desired way.

## Methods used

We use the following methods:

  * *[Krotov's method](https://github.com/JuliaQuantumControl/Krotov.jl)* [[Krotov1996]](references.md#Krotov1996), and
  * [*GRAPE* (*Gradient Ascent Pulse Engineering*)](https://github.com/JuliaQuantumControl/GRAPE.jl) [[KhanejaJMR2005](references.md#KhanejaJMR2005), [FouquieresJMR2011](references.md#FouquieresJMR2011)].

This concludes the document.
====== END =======


=====================================
TEST2: ORIGINAL MD WITH LINKS REMOVED
# Quantum Control

**Quantum optimal control** BrumerShapiro2003;BrifNJP2010;KochJPCM2016;SolaAAMOP2018;MorzhinRMS2019; Wilhelm2003.10132;KochEPJQT2022 attempts to steer a quantum system in some desired way.

## Methods used

We use the following methods:

  * *Krotov's method* Krotov1996, and
  * **GRAPE** (*Gradient Ascent Pulse Engineering*) KhanejaJMR2005;FouquieresJMR2011.

This concludes the document.
====== END =======

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment