changeset 32:3d916aa02d00

Implement basic JSON parser
author Lewin Bormann <lbo@spheniscida.de>
date Sun, 19 Mar 2023 20:04:34 +0100
parents e3af2d3890ce
children 2d033830c26e
files julia/parallel/ParallelProcessing/src/ParallelProcessing.jl julia/parallel/ParallelProcessing/src/jsonparser.jl
diffstat 2 files changed, 180 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/julia/parallel/ParallelProcessing/src/ParallelProcessing.jl	Sun Mar 19 13:09:54 2023 +0100
+++ b/julia/parallel/ParallelProcessing/src/ParallelProcessing.jl	Sun Mar 19 20:04:34 2023 +0100
@@ -1,5 +1,5 @@
 module ParallelProcessing
 
 include("json.jl")
-
+include("jsonparser.jl")
 end # module ParallelProcessing
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/julia/parallel/ParallelProcessing/src/jsonparser.jl	Sun Mar 19 20:04:34 2023 +0100
@@ -0,0 +1,179 @@
+
+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_bool!(jp::JP)::Union{Nothing,Bool}
+    if expect_prefix!(jp, "true")
+        true
+    elseif expect_prefix!(jp, "false")
+        false
+    else
+        nothing
+    end
+end
+
+function take_dict!(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 dict
+            break
+        end
+
+        expect!(jp, ':') || error("malformatted dict - expecting ':'")
+
+        val = take_obj!(jp)
+
+        d[key] = val
+        if expect!(jp, ',')
+            continue
+        else
+            # End of dict
+            break
+        end
+    end
+
+    expect!(jp, '}') || error("Unclosed dict - '}' missing")
+    d
+end
+
+function take_str!(jp::JP)::Union{Nothing,String}
+    expect!(jp, '"') || return nothing
+
+    span = takewhile!(jp, (!=)('"'))
+    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_obj!(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
+
+function take_obj!(jp::JP)::Union{Nothing,Any}
+    d = take_dict!(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
+
+function parse_object(s::AbstractString)
+    jp = JP(s)
+    take_obj!(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