97 lines
2.6 KiB
Julia
97 lines
2.6 KiB
Julia
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||
|
||
mutable struct MD
|
||
content::Vector{Any}
|
||
meta::Dict{Any, Any}
|
||
|
||
MD(content::AbstractVector, meta::Dict = Dict()) =
|
||
new(content, meta)
|
||
end
|
||
|
||
MD(xs...) = MD(vcat(xs...))
|
||
|
||
function MD(cfg::Config, xs...)
|
||
md = MD(xs...)
|
||
md.meta[:config] = cfg
|
||
return md
|
||
end
|
||
|
||
config(md::MD) = md.meta[:config]::Config
|
||
|
||
# Forward some array methods
|
||
|
||
Base.push!(md::MD, x) = push!(md.content, x)
|
||
Base.getindex(md::MD, args...) = md.content[args...]
|
||
Base.setindex!(md::MD, args...) = setindex!(md.content, args...)
|
||
Base.endof(md::MD) = endof(md.content)
|
||
Base.length(md::MD) = length(md.content)
|
||
Base.isempty(md::MD) = isempty(md.content)
|
||
|
||
==(a::MD, b::MD) = (html(a) == html(b))
|
||
|
||
# Parser functions:
|
||
# md – should be modified appropriately
|
||
# return – basically, true if parse was successful
|
||
# false uses the next parser in the queue, true
|
||
# goes back to the beginning
|
||
#
|
||
# Inner parsers:
|
||
# return – element to use or nothing
|
||
|
||
# Inner parsing
|
||
|
||
function parseinline(stream::IO, md::MD, parsers::Vector{Function})
|
||
for parser in parsers
|
||
inner = parser(stream, md)
|
||
inner ≡ nothing || return inner
|
||
end
|
||
end
|
||
|
||
function parseinline(stream::IO, md::MD, config::Config)
|
||
content = []
|
||
buffer = IOBuffer()
|
||
while !eof(stream)
|
||
# FIXME: this is broken if we're looking for non-ASCII
|
||
# characters because peek only returns a single byte.
|
||
char = Char(peek(stream))
|
||
if haskey(config.inner, char) &&
|
||
(inner = parseinline(stream, md, config.inner[char])) !== nothing
|
||
c = String(take!(buffer))
|
||
!isempty(c) && push!(content, c)
|
||
buffer = IOBuffer()
|
||
push!(content, inner)
|
||
else
|
||
write(buffer, read(stream, Char))
|
||
end
|
||
end
|
||
c = String(take!(buffer))
|
||
!isempty(c) && push!(content, c)
|
||
return content
|
||
end
|
||
|
||
parseinline(s::AbstractString, md::MD, c::Config) =
|
||
parseinline(IOBuffer(s), md, c)
|
||
|
||
parseinline(s, md::MD) = parseinline(s, md, config(md))
|
||
|
||
# Block parsing
|
||
|
||
function parse(stream::IO, block::MD, config::Config; breaking = false)
|
||
skipblank(stream)
|
||
eof(stream) && return false
|
||
for parser in (breaking ? config.breaking : [config.breaking; config.regular])
|
||
parser(stream, block) && return true
|
||
end
|
||
return false
|
||
end
|
||
|
||
parse(stream::IO, block::MD; breaking = false) =
|
||
parse(stream, block, config(block), breaking = breaking)
|
||
|
||
function parse(stream::IO; flavor = julia)
|
||
isa(flavor, Symbol) && (flavor = flavors[flavor])
|
||
markdown = MD(flavor)
|
||
while parse(stream, markdown, flavor) end
|
||
return markdown
|
||
end
|