Add: julia-0.6.2
Former-commit-id: ccc667cf67d569f3fb3df39aa57c2134755a7551
This commit is contained in:
82
julia-0.6.2/share/julia/base/markdown/parse/config.jl
Normal file
82
julia-0.6.2/share/julia/base/markdown/parse/config.jl
Normal file
@@ -0,0 +1,82 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
const InnerConfig = Dict{Char, Vector{Function}}
|
||||
|
||||
mutable struct Config
|
||||
breaking::Vector{Function}
|
||||
regular::Vector{Function}
|
||||
inner::InnerConfig
|
||||
end
|
||||
|
||||
Config() = Config(Function[], Function[], InnerConfig())
|
||||
|
||||
const META = Dict{Function, Dict{Symbol, Any}}()
|
||||
|
||||
getset(coll, key, default) = coll[key] = get(coll, key, default)
|
||||
|
||||
meta(f) = getset(META, f, Dict{Symbol, Any}())
|
||||
|
||||
breaking!(f) = meta(f)[:breaking] = true
|
||||
breaking(f) = get(meta(f), :breaking, false)
|
||||
|
||||
triggers!(f, ts) = meta(f)[:triggers] = Set{Char}(ts)
|
||||
triggers(f) = get(meta(f), :triggers, Set{Char}())
|
||||
|
||||
# Macros
|
||||
|
||||
isexpr(x::Expr, ts...) = x.head in ts
|
||||
isexpr(x::T, ts...) where {T} = T in ts
|
||||
|
||||
macro breaking(ex)
|
||||
isexpr(ex, :->) || error("invalid @breaking form, use ->")
|
||||
b, def = ex.args
|
||||
if b
|
||||
quote
|
||||
f = $(esc(def))
|
||||
breaking!(f)
|
||||
f
|
||||
end
|
||||
else
|
||||
esc(def)
|
||||
end
|
||||
end
|
||||
|
||||
macro trigger(ex)
|
||||
isexpr(ex, :->) || error("invalid @triggers form, use ->")
|
||||
ts, def = ex.args
|
||||
quote
|
||||
f = $(esc(def))
|
||||
triggers!(f, $ts)
|
||||
f
|
||||
end
|
||||
end
|
||||
|
||||
# Construction
|
||||
|
||||
function config(parsers::Function...)
|
||||
c = Config()
|
||||
for parser in parsers
|
||||
ts = triggers(parser)
|
||||
if breaking(parser)
|
||||
push!(c.breaking, parser)
|
||||
elseif !isempty(ts)
|
||||
for t in ts
|
||||
push!(getset(c.inner, t, Function[]), parser)
|
||||
end
|
||||
else
|
||||
push!(c.regular, parser)
|
||||
end
|
||||
end
|
||||
return c
|
||||
end
|
||||
|
||||
# Flavour definitions
|
||||
|
||||
const flavors = Dict{Symbol, Config}()
|
||||
|
||||
macro flavor(name, features)
|
||||
quote
|
||||
const $(esc(name)) = config($(map(esc,features.args)...))
|
||||
flavors[$(Expr(:quote, name))] = $(esc(name))
|
||||
end
|
||||
end
|
||||
96
julia-0.6.2/share/julia/base/markdown/parse/parse.jl
Normal file
96
julia-0.6.2/share/julia/base/markdown/parse/parse.jl
Normal file
@@ -0,0 +1,96 @@
|
||||
# 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
|
||||
206
julia-0.6.2/share/julia/base/markdown/parse/util.jl
Normal file
206
julia-0.6.2/share/julia/base/markdown/parse/util.jl
Normal file
@@ -0,0 +1,206 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
import Base: peek
|
||||
|
||||
macro dotimes(n, body)
|
||||
quote
|
||||
for i = 1:$(esc(n))
|
||||
$(esc(body))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
const whitespace = " \t\r"
|
||||
|
||||
"""
|
||||
Skip any leading whitespace. Returns io.
|
||||
"""
|
||||
function skipwhitespace(io::IO; newlines = true)
|
||||
while !eof(io) && (Char(peek(io)) in whitespace || (newlines && peek(io) == UInt8('\n')))
|
||||
read(io, Char)
|
||||
end
|
||||
return io
|
||||
end
|
||||
|
||||
"""
|
||||
Skip any leading blank lines. Returns the number skipped.
|
||||
"""
|
||||
function skipblank(io::IO)
|
||||
start = position(io)
|
||||
i = 0
|
||||
while !eof(io)
|
||||
c = read(io, Char)
|
||||
c == '\n' && (start = position(io); i+=1; continue)
|
||||
c == '\r' && (start = position(io); i+=1; continue)
|
||||
c in whitespace || break
|
||||
end
|
||||
seek(io, start)
|
||||
return i
|
||||
end
|
||||
|
||||
"""
|
||||
Returns true if the line contains only (and, unless allowempty,
|
||||
at least one of) the characters given.
|
||||
"""
|
||||
function linecontains(io::IO, chars; allow_whitespace = true,
|
||||
eat = true,
|
||||
allowempty = false)
|
||||
start = position(io)
|
||||
l = readline(io)
|
||||
length(l) == 0 && return allowempty
|
||||
|
||||
result = allowempty
|
||||
for c in l
|
||||
c in whitespace && (allow_whitespace ? continue : (result = false; break))
|
||||
c in chars && (result = true; continue)
|
||||
result = false; break
|
||||
end
|
||||
!(result && eat) && seek(io, start)
|
||||
return result
|
||||
end
|
||||
|
||||
blankline(io::IO; eat = true) =
|
||||
linecontains(io, "",
|
||||
allow_whitespace = true,
|
||||
allowempty = true,
|
||||
eat = eat)
|
||||
|
||||
"""
|
||||
Test if the stream starts with the given string.
|
||||
`eat` specifies whether to advance on success (true by default).
|
||||
`padding` specifies whether leading whitespace should be ignored.
|
||||
"""
|
||||
function startswith(stream::IO, s::AbstractString; eat = true, padding = false, newlines = true)
|
||||
start = position(stream)
|
||||
padding && skipwhitespace(stream, newlines = newlines)
|
||||
result = true
|
||||
for char in s
|
||||
!eof(stream) && read(stream, Char) == char ||
|
||||
(result = false; break)
|
||||
end
|
||||
!(result && eat) && seek(stream, start)
|
||||
return result
|
||||
end
|
||||
|
||||
function startswith(stream::IO, c::Char; eat = true)
|
||||
if !eof(stream) && peek(stream) == UInt8(c)
|
||||
eat && read(stream, Char)
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function startswith(stream::IO, ss::Vector{<:AbstractString}; kws...)
|
||||
any(s->startswith(stream, s; kws...), ss)
|
||||
end
|
||||
|
||||
function startswith(stream::IO, r::Regex; eat = true, padding = false)
|
||||
@assert Base.startswith(r.pattern, "^")
|
||||
start = position(stream)
|
||||
padding && skipwhitespace(stream)
|
||||
line = readline(stream)
|
||||
seek(stream, start)
|
||||
m = match(r, line)
|
||||
m === nothing && return ""
|
||||
eat && @dotimes length(m.match) read(stream, Char)
|
||||
return m.match
|
||||
end
|
||||
|
||||
"""
|
||||
Executes the block of code, and if the return value is `nothing`,
|
||||
returns the stream to its initial position.
|
||||
"""
|
||||
function withstream(f, stream)
|
||||
pos = position(stream)
|
||||
result = f()
|
||||
(result ≡ nothing || result ≡ false) && seek(stream, pos)
|
||||
return result
|
||||
end
|
||||
|
||||
"""
|
||||
Consume the standard allowed markdown indent of
|
||||
three spaces. Returns false if there are more than
|
||||
three present.
|
||||
"""
|
||||
function eatindent(io::IO, n = 3)
|
||||
withstream(io) do
|
||||
m = 0
|
||||
while startswith(io, ' ') m += 1 end
|
||||
return m <= n
|
||||
end
|
||||
end
|
||||
|
||||
"""
|
||||
Read the stream until startswith(stream, delim)
|
||||
The delimiter is consumed but not included.
|
||||
Returns nothing and resets the stream if delim is
|
||||
not found.
|
||||
"""
|
||||
function readuntil(stream::IO, delimiter; newlines = false, match = nothing)
|
||||
withstream(stream) do
|
||||
buffer = IOBuffer()
|
||||
count = 0
|
||||
while !eof(stream)
|
||||
if startswith(stream, delimiter)
|
||||
if count == 0
|
||||
return String(take!(buffer))
|
||||
else
|
||||
count -= 1
|
||||
write(buffer, delimiter)
|
||||
continue
|
||||
end
|
||||
end
|
||||
char = read(stream, Char)
|
||||
char == match && (count += 1)
|
||||
!newlines && char == '\n' && break
|
||||
write(buffer, char)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: refactor this. If we're going to assume
|
||||
# the delimiter is a single character + a minimum
|
||||
# repeat we may as well just pass that into the
|
||||
# function.
|
||||
|
||||
"""
|
||||
Parse a symmetrical delimiter which wraps words.
|
||||
i.e. `*word word*` but not `*word * word`.
|
||||
`repeat` specifies whether the delimiter can be repeated.
|
||||
Escaped delimiters are not yet supported.
|
||||
"""
|
||||
function parse_inline_wrapper(stream::IO, delimiter::AbstractString; rep = false)
|
||||
delimiter, nmin = string(delimiter[1]), length(delimiter)
|
||||
withstream(stream) do
|
||||
if position(stream) >= 1
|
||||
# check the previous byte isn't a delimiter
|
||||
skip(stream, -1)
|
||||
(read(stream, Char) in delimiter) && return nothing
|
||||
end
|
||||
n = nmin
|
||||
startswith(stream, delimiter^n) || return nothing
|
||||
while startswith(stream, delimiter); n += 1; end
|
||||
!rep && n > nmin && return nothing
|
||||
!eof(stream) && Char(peek(stream)) in whitespace && return nothing
|
||||
|
||||
buffer = IOBuffer()
|
||||
while !eof(stream)
|
||||
char = read(stream, Char)
|
||||
write(buffer, char)
|
||||
if !(char in whitespace || char == '\n' || char in delimiter) && startswith(stream, delimiter^n)
|
||||
trailing = 0
|
||||
while startswith(stream, delimiter); trailing += 1; end
|
||||
trailing == 0 && return String(take!(buffer))
|
||||
write(buffer, delimiter ^ (n + trailing))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function showrest(io::IO)
|
||||
start = position(io)
|
||||
show(readstring(io))
|
||||
println()
|
||||
seek(io, start)
|
||||
end
|
||||
Reference in New Issue
Block a user