view julia/parallel/ParallelProcessing/src/jsonparser.jl @ 33:2d033830c26e

Implement struct-specific parser
author Lewin Bormann <lbo@spheniscida.de>
date Sun, 19 Mar 2023 20:20:13 +0100
parents 3d916aa02d00
children a36e3e66ae3e
line wrap: on
line source


mutable struct JP
    s::String
    pos::Int
end

function JP(s::AbstractString)::JP
    JP(string(s), 1)
end

function current(jp::JP)::Char
    jp.s[jp.pos]
end

function next!(jp::JP)
    jp.pos += 1
end

function isend(jp::JP)
    jp.pos > length(jp.s)
end

function take_num!(jp::JP)::Union{Nothing,Float64}
    pred(c) = isdigit(c) || (c == '.') # todo: exponential numbers
    span = takewhile!(jp, pred)
    if isnothing(span)
        nothing
    else
        a, b = span
        parse(Float64, (@view jp.s[a:b]))
    end
end

function take_num!(jp::JP)::Union{Nothing,Float64,Int}
    pred(c) = isdigit(c) || (c == '-') || (c == '.') # todo: exponential numbers
    span = takewhile!(jp, pred)
    if isnothing(span)
        nothing
    else
        a, b = span
        s = (@view jp.s[a:b])
        if contains(s, '.')
            parse(Float64, s)
        else
            parse(Int, s)
        end
    end
end

function take_bool!(jp::JP)::Union{Nothing,Bool}
    if expect_prefix!(jp, "true")
        true
    elseif expect_prefix!(jp, "false")
        false
    else
        nothing
    end
end

function take_object!(jp::JP)::Union{Nothing,Dict{String,Any}}
    expect!(jp, '{') || return nothing

    d = Dict{String,Any}()
    while true
        key = take_str!(jp)
        if isnothing(key)
            # Empty object
            break
        end

        expect!(jp, ':') || error("malformatted object - expecting ':'")

        val = take_val!(jp)

        d[key] = val
        if expect!(jp, ',')
            continue
        else
            # End of object
            break
        end
    end

    expect!(jp, '}') || error("Unclosed object - '}' missing")
    d
end

function take_str!(jp::JP)::Union{Nothing,String}
    expect!(jp, '"') || return nothing

    span = takewhile!(jp, (!=)('"'), false)
    if isnothing(span)
        error("unclosed string at $(jp.pos)")
    end

    expect!(jp, '"') || error("unclosed string at $(jp.pos)")
    a, b = span
    jp.s[a:b]
end

function take_list!(jp::JP)::Union{Nothing,Vector{Any}}
    expect!(jp, '[') || return nothing

    l = Any[]
    while true
        o = take_val!(jp)
        if isnothing(o)
            break
        else
            push!(l, o)
        end

        if expect!(jp, ',')
            continue
        else
            break
        end
    end

    expect!(jp, ']') || error("Missing closing ']' at $(jp.pos)")
    l
end

"""value is anything - object/list/number/boolean/string"""
function take_val!(jp::JP)::Union{Nothing,Any}
    d = take_object!(jp)
    if !isnothing(d)
        return d
    end
    n = take_num!(jp)
    if !isnothing(n)
        return n
    end
    s = take_str!(jp)
    if !isnothing(s)
        return s
    end
    b = take_bool!(jp)
    if !isnothing(b)
        return b
    end
    l = take_list!(jp)
    if !isnothing(l)
        return l
    end
    nothing
end

"""Parse a json value.."""
function parse_value(s::AbstractString)
    jp = JP(s)
    take_val!(jp)
end

function takewhile!(jp::JP, pred::Function, stripws=true)::Union{Nothing,Tuple{Int,Int}}
    if stripws
        strip_ws!(jp)
    end
    if !isend(jp) && pred(current(jp))
        a = jp.pos
        b = a
        while !isend(jp) && pred(current(jp))
            next!(jp)
            b += 1
        end
        (a, b-1)
    else
        nothing
    end
end

function strip_ws!(jp::JP)
    while !isend(jp) && isspace(jp.s[jp.pos])
        jp.pos += 1
    end
end

function expect!(jp::JP, c::Char)::Bool
    strip_ws!(jp)
    if current(jp) == c
        next!(jp)
        true
    else
        false
    end
end

function expect_prefix!(jp::JP, pref::AbstractString)::Bool
    strip_ws!(jp)

    if (@view jp.s[jp.pos:min(length(jp.s), jp.pos+length(pref)-1)]) == pref
        jp.pos += length(pref)
        true
    else
        false
    end
end