Mercurial > lbo > hg > juliaplay
changeset 36:03d20a4dd8f2
json: metaparser
author | Lewin Bormann <lbo@spheniscida.de> |
---|---|
date | Fri, 24 Mar 2023 21:30:03 +0100 |
parents | 8208faa6c42f |
children | 37a528750178 |
files | julia/parallel/ParallelProcessing/src/ParallelProcessing.jl julia/parallel/ParallelProcessing/src/json.jl julia/parallel/ParallelProcessing/src/jsonparser.jl julia/parallel/ParallelProcessing/src/metaparser.jl |
diffstat | 4 files changed, 193 insertions(+), 41 deletions(-) [+] |
line wrap: on
line diff
--- a/julia/parallel/ParallelProcessing/src/ParallelProcessing.jl Fri Mar 24 20:04:38 2023 +0100 +++ b/julia/parallel/ParallelProcessing/src/ParallelProcessing.jl Fri Mar 24 21:30:03 2023 +0100 @@ -2,4 +2,5 @@ include("jsonparser.jl") include("json.jl") +include("metaparser.jl") end # module ParallelProcessing
--- a/julia/parallel/ParallelProcessing/src/json.jl Fri Mar 24 20:04:38 2023 +0100 +++ b/julia/parallel/ParallelProcessing/src/json.jl Fri Mar 24 21:30:03 2023 +0100 @@ -13,7 +13,7 @@ expect!(jp, '{') || return nothing level::Union{Nothing,String} = nothing - kind::Union{Nothing, Int} = nothing + kind::Union{Nothing,Int} = nothing while true key = take_str!(jp) if isnothing(key) @@ -44,7 +44,7 @@ end expect!(jp, '}') || error("unclosed Details object") - + Details(level, kind) end @@ -62,33 +62,38 @@ SimpleEntry(s, a, f, Details(level, kind)) end -function generate_json(file, n=1000) - open(file; write=true) do fh +function generate_json(file, n = 1000) + open(file; write = true) do fh for i = 1:n println(fh, json(generate_entry())) end end end -fib(n) = if n <= 2 1 else fib(n-1) + fib(n-2) end +fib(n) = + if n <= 2 + 1 + else + fib(n - 1) + fib(n - 2) + end function expensive_mapper(m::Dict)::Int i = abs(m["a"]) % 35 fib(i) end -function process_json(file, mapper=x -> ())::Vector - open(file; read=true) do fh - [mapper(JSON.parse(line)) for line = eachline(fh)] +function process_json(file, mapper = x -> ())::Vector + open(file; read = true) do fh + [mapper(JSON.parse(line)) for line in eachline(fh)] end end -function process_json_parallel(file, mapper=x -> ())::Vector - open(file; read=true) do fh +function process_json_parallel(file, mapper = x -> ())::Vector + open(file; read = true) do fh ch = Channel(100) count = 0 - for line = eachline(fh) + for line in eachline(fh) Threads.@spawn put!(ch, mapper(JSON.parse(line))) count += 1 end
--- a/julia/parallel/ParallelProcessing/src/jsonparser.jl Fri Mar 24 20:04:38 2023 +0100 +++ b/julia/parallel/ParallelProcessing/src/jsonparser.jl Fri Mar 24 21:30:03 2023 +0100 @@ -1,11 +1,13 @@ +# JSON parser struct. mutable struct JP s::String pos::Int + length::Int end @inline function JP(s::AbstractString)::JP - JP(string(s), 1) + JP(string(s), 1, length(s)) end @inline function current(jp::JP)::Char @@ -17,33 +19,25 @@ end @inline function isend(jp::JP) - jp.pos > length(jp.s) + jp.pos > jp.length 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 +@inline function reset!(jp::JP) + jp.pos = 1 end +const FLOAT_CHARS = ['e', '.', '-'] + function take_num!(jp::JP)::Union{Nothing,Float64,Int} - pred(c) = isdigit(c) || (c == '-') || (c == '.') # todo: exponential numbers + isfloatstr(c) = c in FLOAT_CHARS + pred(c) = isdigit(c) || isfloatstr(c) 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 + parse(Float64, s) end end @@ -123,10 +117,6 @@ """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 @@ -135,14 +125,18 @@ if !isnothing(s) return s end + l = take_list!(jp) + if !isnothing(l) + return l + end + d = take_object!(jp) + if !isnothing(d) + return d + end b = take_bool!(jp) if !isnothing(b) return b end - l = take_list!(jp) - if !isnothing(l) - return l - end nothing end @@ -152,13 +146,17 @@ take_val!(jp) end +function parse_struct(t::Type{T}, s::AbstractString)::T where {T} + take_struct!(t, JP(s)) +end + function strip_ws!(jp::JP) while !isend(jp) && isspace(jp.s[jp.pos]) jp.pos += 1 end end -function takewhile!(jp::JP, pred::Function, stripws=true)::Union{Nothing,Tuple{Int,Int}} +function takewhile!(jp::JP, pred::Function, stripws = true)::Union{Nothing,Tuple{Int,Int}} if stripws strip_ws!(jp) end @@ -167,13 +165,13 @@ while !isend(jp) && pred(current(jp)) next!(jp) end - (a, jp.pos-1) + (a, jp.pos - 1) else nothing end end -function expect!(jp::JP, c::Char)::Bool +@inline function expect!(jp::JP, c::Char)::Bool strip_ws!(jp) if current(jp) == c next!(jp) @@ -185,9 +183,10 @@ function expect_prefix!(jp::JP, pref::AbstractString)::Bool strip_ws!(jp) + pl = length(pref) - if (@view jp.s[jp.pos:min(length(jp.s), jp.pos+length(pref)-1)]) == pref - jp.pos += length(pref) + if (@view jp.s[jp.pos:min(jp.length, jp.pos + pl - 1)]) == pref + jp.pos += pl true else false
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/julia/parallel/ParallelProcessing/src/metaparser.jl Fri Mar 24 21:30:03 2023 +0100 @@ -0,0 +1,147 @@ + +function parse_struct(s::Expr) + s.head == :struct || error("Expr must be struct but is $s") + args = s.args[2:end] + typedef = args[1] + + fields = args[2] + fields.head == :block || error("Expr must contain a block but has $fields") + fields_exprs = Pair[] + for f in fields.args + if typeof(f) == LineNumberNode + continue + end + typeof(f) == Expr || error("Field $f in $typedef must have type!") + f.head == :(::) || error( + "Field $f in $typedef must be type! (we don't support constructors etc. - struct must be plain)", + ) + name, typ = f.args + typeof(typ) == Symbol || + error("Type of $f should be symbol (simple type) but is $(typeof(typ)): $typ") + typ = eval(typ) + isconcretetype(typ) || + error("Type of field $name must be concrete, but is not (is $typ)!") + push!(fields_exprs, name => eval(typ)) + end + + fields_exprs +end + +function get_type_of_struct(s::Expr) + s.head == :struct || error("Expr must be struct, is $s") + typedef = s.args[2] + + if typeof(typedef) == Symbol + typedef + elseif typeof(typedef) == Expr + error("We don't support generic structs yet :(") + end +end + +function json_parseable(strct) + typs::Vector{Pair{Symbol,Type}} = parse_struct(strct) + typ = get_type_of_struct(strct) + + fieldvars = [:($(name)::Union{$typ,Nothing} = nothing) for (name, typ) in typs] + + method_map = Dict( + Int64 => :take_num!, + Float64 => :take_num!, + String => :take_str!, + Dict => :take_object!, + Bool => :take_bool!, + ) + methods = [(name, method_map[typ]) for (name, typ) in typs] + field_dispatch = [ + quote + if !matched && key == $(string(name)) + $name = $(method)(jp) + matched = true + end + end for (name, method) in methods + ] + + quote + $strct + + function ParallelProcessing.take_struct!( + ::Type{$(esc(typ))}, + jp::JP, + )::Union{Nothing,$typ} + expect!(jp, '{') || return nothing + + $(fieldvars...) + + while true + key = take_str!(jp) + if isnothing(key) + break + end + + matched = false + $(field_dispatch...) + + if !matched + take_val!(jp) + end + + if expect!(jp, ',') + continue + else + break + end + end + + if isnothing(level) || isnothing(kind) + error("Elements missing from object: $level, $kind") + end + + expect!(jp, '}') || error("unclosed Details object") + + Details(level, kind) + end + end +end + +macro json_parseable(strct) + json_parseable(strct) +end + +function take_struct_!(::Type{Details}, jp::JP)::Union{Nothing,Details} + expect!(jp, '{') || return nothing + + level::Union{Nothing,String} = nothing + kind::Union{Nothing,Int} = nothing + while true + key = take_str!(jp) + if isnothing(key) + break + end + + expect!(jp, ':') || error("malformed object - expected ':'") + + # Custom code! + if key == "level" + level = take_str!(jp) + elseif key == "kind" + kind = take_num!(jp) + else + # Ignore unknown keys + take_val!(jp) + end + + if expect!(jp, ',') + continue + else + break + end + end + + if isnothing(level) || isnothing(kind) + error("Elements missing from object: $level, $kind") + end + + expect!(jp, '}') || error("unclosed Details object") + + Details(level, kind) +end